001package org.hl7.fhir.r4.model; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.HashSet; 006import java.util.List; 007import java.util.Set; 008 009import org.hl7.fhir.r4.context.IWorkerContext; 010import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 011import org.hl7.fhir.r4.model.ExpressionNode.CollectionStatus; 012import org.hl7.fhir.exceptions.DefinitionException; 013import org.hl7.fhir.utilities.Utilities; 014 015 016 017public class TypeDetails { 018 public static final String FHIR_NS = "http://hl7.org/fhir/StructureDefinition/"; 019 public static final String FP_NS = "http://hl7.org/fhirpath/"; 020 public static final String FP_String = "http://hl7.org/fhirpath/String"; 021 public static final String FP_Boolean = "http://hl7.org/fhirpath/Boolean"; 022 public static final String FP_Integer = "http://hl7.org/fhirpath/Integer"; 023 public static final String FP_Decimal = "http://hl7.org/fhirpath/Decimal"; 024 public static final String FP_Quantity = "http://hl7.org/fhirpath/Quantity"; 025 public static final String FP_DateTime = "http://hl7.org/fhirpath/DateTime"; 026 public static final String FP_Time = "http://hl7.org/fhirpath/Time"; 027 public static final String FP_SimpleTypeInfo = "http://hl7.org/fhirpath/SimpleTypeInfo"; 028 public static final String FP_ClassInfo = "http://hl7.org/fhirpath/ClassInfo"; 029 030 public static class ProfiledType { 031 private String uri; 032 private List<String> profiles; // or, not and 033 private List<ElementDefinitionBindingComponent> bindings; 034 035 public ProfiledType(String n) { 036 uri = ns(n); 037 } 038 039 public String getUri() { 040 return uri; 041 } 042 043 public boolean hasProfiles() { 044 return profiles != null && profiles.size() > 0; 045 } 046 public List<String> getProfiles() { 047 return profiles; 048 } 049 050 public boolean hasBindings() { 051 return bindings != null && bindings.size() > 0; 052 } 053 public List<ElementDefinitionBindingComponent> getBindings() { 054 return bindings; 055 } 056 057 public static String ns(String n) { 058 return Utilities.isAbsoluteUrl(n) ? n : FHIR_NS+n; 059 } 060 061 public void addProfile(String profile) { 062 if (profiles == null) 063 profiles = new ArrayList<String>(); 064 profiles.add(profile); 065 } 066 067 public void addBinding(ElementDefinitionBindingComponent binding) { 068 bindings = new ArrayList<ElementDefinitionBindingComponent>(); 069 bindings.add(binding); 070 } 071 072 public boolean hasBinding(ElementDefinitionBindingComponent b) { 073 return false; // todo: do we need to do this? 074 } 075 076 public void addProfiles(List<CanonicalType> list) { 077 if (profiles == null) 078 profiles = new ArrayList<String>(); 079 for (UriType u : list) 080 profiles.add(u.getValue()); 081 } 082 public boolean isSystemType() { 083 return uri.startsWith(FP_NS); 084 } 085 } 086 087 private List<ProfiledType> types = new ArrayList<ProfiledType>(); 088 private CollectionStatus collectionStatus; 089 public TypeDetails(CollectionStatus collectionStatus, String... names) { 090 super(); 091 this.collectionStatus = collectionStatus; 092 for (String n : names) { 093 this.types.add(new ProfiledType(n)); 094 } 095 } 096 public TypeDetails(CollectionStatus collectionStatus, Set<String> names) { 097 super(); 098 this.collectionStatus = collectionStatus; 099 for (String n : names) { 100 addType(new ProfiledType(n)); 101 } 102 } 103 public TypeDetails(CollectionStatus collectionStatus, ProfiledType pt) { 104 super(); 105 this.collectionStatus = collectionStatus; 106 this.types.add(pt); 107 } 108 public String addType(String n) { 109 ProfiledType pt = new ProfiledType(n); 110 String res = pt.uri; 111 addType(pt); 112 return res; 113 } 114 public String addType(String n, String p) { 115 ProfiledType pt = new ProfiledType(n); 116 pt.addProfile(p); 117 String res = pt.uri; 118 addType(pt); 119 return res; 120 } 121 public void addType(ProfiledType pt) { 122 for (ProfiledType et : types) { 123 if (et.uri.equals(pt.uri)) { 124 if (pt.profiles != null) { 125 for (String p : pt.profiles) { 126 if (et.profiles == null) 127 et.profiles = new ArrayList<String>(); 128 if (!et.profiles.contains(p)) 129 et.profiles.add(p); 130 } 131 } 132 if (pt.bindings != null) { 133 for (ElementDefinitionBindingComponent b : pt.bindings) { 134 if (et.bindings == null) 135 et.bindings = new ArrayList<ElementDefinitionBindingComponent>(); 136 if (!et.hasBinding(b)) 137 et.bindings.add(b); 138 } 139 } 140 return; 141 } 142 } 143 types.add(pt); 144 } 145 146 public void addTypes(Collection<String> names) { 147 for (String n : names) 148 addType(new ProfiledType(n)); 149 } 150 151 public boolean hasType(IWorkerContext context, String... tn) { 152 for (String n: tn) { 153 String t = ProfiledType.ns(n); 154 if (typesContains(t)) 155 return true; 156 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) { 157 t = FP_NS+Utilities.capitalize(n); 158 if (typesContains(t)) 159 return true; 160 } 161 } 162 for (String n: tn) { 163 String id = n.contains("#") ? n.substring(0, n.indexOf("#")) : n; 164 String tail = null; 165 if (n.contains("#")) { 166 tail = n.substring( n.indexOf("#")+1); 167 tail = tail.substring(tail.indexOf(".")); 168 } 169 String t = ProfiledType.ns(n); 170 StructureDefinition sd = context.fetchResource(StructureDefinition.class, t); 171 while (sd != null) { 172 if (tail == null && typesContains(sd.getUrl())) 173 return true; 174 if (tail == null && getSystemType(sd.getUrl()) != null && typesContains(getSystemType(sd.getUrl()))) 175 return true; 176 if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail)) 177 return true; 178 if (sd.hasBaseDefinition()) { 179 if (sd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Element") && !sd.getType().equals("string") && sd.getType().equals("uri")) 180 sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string"); 181 else 182 sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 183 } else 184 sd = null; 185 } 186 } 187 return false; 188 } 189 190 private String getSystemType(String url) { 191 if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) { 192 String code = url.substring(40); 193 if (Utilities.existsInList(code, "string", "boolean", "integer", "decimal", "dateTime", "time", "Quantity")) 194 return FP_NS+Utilities.capitalize(code); 195 } 196 return null; 197 } 198 199 private boolean typesContains(String t) { 200 for (ProfiledType pt : types) 201 if (pt.uri.equals(t)) 202 return true; 203 return false; 204 } 205 206 public void update(TypeDetails source) { 207 for (ProfiledType pt : source.types) 208 addType(pt); 209 if (collectionStatus == null) 210 collectionStatus = source.collectionStatus; 211 else if (source.collectionStatus == CollectionStatus.UNORDERED) 212 collectionStatus = source.collectionStatus; 213 else 214 collectionStatus = CollectionStatus.ORDERED; 215 } 216 public TypeDetails union(TypeDetails right) { 217 TypeDetails result = new TypeDetails(null); 218 if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED) 219 result.collectionStatus = CollectionStatus.UNORDERED; 220 else 221 result.collectionStatus = CollectionStatus.ORDERED; 222 for (ProfiledType pt : types) 223 result.addType(pt); 224 for (ProfiledType pt : right.types) 225 result.addType(pt); 226 return result; 227 } 228 229 public TypeDetails intersect(TypeDetails right) { 230 TypeDetails result = new TypeDetails(null); 231 if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED) 232 result.collectionStatus = CollectionStatus.UNORDERED; 233 else 234 result.collectionStatus = CollectionStatus.ORDERED; 235 for (ProfiledType pt : types) { 236 boolean found = false; 237 for (ProfiledType r : right.types) 238 found = found || pt.uri.equals(r.uri); 239 if (found) 240 result.addType(pt); 241 } 242 for (ProfiledType pt : right.types) 243 result.addType(pt); 244 return result; 245 } 246 247 public boolean hasNoTypes() { 248 return types.isEmpty(); 249 } 250 public Set<String> getTypes() { 251 Set<String> res = new HashSet<String>(); 252 for (ProfiledType pt : types) 253 res.add(pt.uri); 254 return res; 255 } 256 public TypeDetails toSingleton() { 257 TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON); 258 result.types.addAll(types); 259 return result; 260 } 261 public CollectionStatus getCollectionStatus() { 262 return collectionStatus; 263 } 264 public boolean hasType(String n) { 265 String t = ProfiledType.ns(n); 266 if (typesContains(t)) 267 return true; 268 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) { 269 t = FP_NS+Utilities.capitalize(n); 270 if (typesContains(t)) 271 return true; 272 } 273 return false; 274 } 275 276 public boolean hasType(Set<String> tn) { 277 for (String n: tn) { 278 String t = ProfiledType.ns(n); 279 if (typesContains(t)) 280 return true; 281 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) { 282 t = FP_NS+Utilities.capitalize(n); 283 if (typesContains(t)) 284 return true; 285 } 286 } 287 return false; 288 } 289 public String describe() { 290 return getTypes().toString(); 291 } 292 public String getType() { 293 for (ProfiledType pt : types) 294 return pt.uri; 295 return null; 296 } 297 @Override 298 public String toString() { 299 return (collectionStatus == null ? collectionStatus.SINGLETON.toString() : collectionStatus.toString()) + getTypes().toString(); 300 } 301 public String getTypeCode() throws DefinitionException { 302 if (types.size() != 1) 303 throw new DefinitionException("Multiple types? ("+types.toString()+")"); 304 for (ProfiledType pt : types) 305 if (pt.uri.startsWith("http://hl7.org/fhir/StructureDefinition/")) 306 return pt.uri.substring(40); 307 else 308 return pt.uri; 309 return null; 310 } 311 public List<ProfiledType> getProfiledTypes() { 312 return types; 313 } 314 public boolean hasBinding() { 315 for (ProfiledType pt : types) { 316 if (pt.hasBindings()) 317 return true; 318 } 319 return false; 320 } 321 public ElementDefinitionBindingComponent getBinding() { 322 for (ProfiledType pt : types) { 323 for (ElementDefinitionBindingComponent b : pt.getBindings()) 324 return b; 325 } 326 return null; 327 } 328 329 330}