001package org.hl7.fhir.r4.model; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Collection; 035import java.util.HashSet; 036import java.util.List; 037import java.util.Set; 038 039import org.hl7.fhir.exceptions.DefinitionException; 040import org.hl7.fhir.r4.context.IWorkerContext; 041import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 042import org.hl7.fhir.r4.model.ExpressionNode.CollectionStatus; 043import org.hl7.fhir.utilities.Utilities; 044 045public class TypeDetails { 046 public static final String FHIR_NS = "http://hl7.org/fhir/StructureDefinition/"; 047 public static final String FP_NS = "http://hl7.org/fhirpath/"; 048 public static final String FP_String = "http://hl7.org/fhirpath/String"; 049 public static final String FP_Boolean = "http://hl7.org/fhirpath/Boolean"; 050 public static final String FP_Integer = "http://hl7.org/fhirpath/Integer"; 051 public static final String FP_Decimal = "http://hl7.org/fhirpath/Decimal"; 052 public static final String FP_Quantity = "http://hl7.org/fhirpath/Quantity"; 053 public static final String FP_DateTime = "http://hl7.org/fhirpath/DateTime"; 054 public static final String FP_Time = "http://hl7.org/fhirpath/Time"; 055 public static final String FP_SimpleTypeInfo = "http://hl7.org/fhirpath/SimpleTypeInfo"; 056 public static final String FP_ClassInfo = "http://hl7.org/fhirpath/ClassInfo"; 057 public static final Set<String> FP_NUMBERS = new HashSet<String>(Arrays.asList(FP_Integer, FP_Decimal)); 058 059 public static class ProfiledType { 060 private String uri; 061 private List<String> profiles; // or, not and 062 private List<ElementDefinitionBindingComponent> bindings; 063 064 public ProfiledType(String n) { 065 uri = ns(n); 066 } 067 068 public String getUri() { 069 return uri; 070 } 071 072 public boolean hasProfiles() { 073 return profiles != null && profiles.size() > 0; 074 } 075 076 public List<String> getProfiles() { 077 return profiles; 078 } 079 080 public boolean hasBindings() { 081 return bindings != null && bindings.size() > 0; 082 } 083 084 public List<ElementDefinitionBindingComponent> getBindings() { 085 return bindings; 086 } 087 088 public static String ns(String n) { 089 return Utilities.isAbsoluteUrl(n) ? n : FHIR_NS + n; 090 } 091 092 public void addProfile(String profile) { 093 if (profiles == null) 094 profiles = new ArrayList<String>(); 095 profiles.add(profile); 096 } 097 098 public void addBinding(ElementDefinitionBindingComponent binding) { 099 bindings = new ArrayList<ElementDefinitionBindingComponent>(); 100 bindings.add(binding); 101 } 102 103 public boolean hasBinding(ElementDefinitionBindingComponent b) { 104 return false; // todo: do we need to do this? 105 } 106 107 public void addProfiles(List<CanonicalType> list) { 108 if (profiles == null) 109 profiles = new ArrayList<String>(); 110 for (UriType u : list) 111 profiles.add(u.getValue()); 112 } 113 114 public boolean isSystemType() { 115 return uri.startsWith(FP_NS); 116 } 117 } 118 119 private List<ProfiledType> types = new ArrayList<ProfiledType>(); 120 private CollectionStatus collectionStatus; 121 122 public TypeDetails(CollectionStatus collectionStatus, String... names) { 123 super(); 124 this.collectionStatus = collectionStatus; 125 for (String n : names) { 126 this.types.add(new ProfiledType(n)); 127 } 128 } 129 130 public TypeDetails(CollectionStatus collectionStatus, Set<String> names) { 131 super(); 132 this.collectionStatus = collectionStatus; 133 for (String n : names) { 134 addType(new ProfiledType(n)); 135 } 136 } 137 138 public TypeDetails(CollectionStatus collectionStatus, ProfiledType pt) { 139 super(); 140 this.collectionStatus = collectionStatus; 141 this.types.add(pt); 142 } 143 144 public String addType(String n) { 145 ProfiledType pt = new ProfiledType(n); 146 String res = pt.uri; 147 addType(pt); 148 return res; 149 } 150 151 public String addType(String n, String p) { 152 ProfiledType pt = new ProfiledType(n); 153 pt.addProfile(p); 154 String res = pt.uri; 155 addType(pt); 156 return res; 157 } 158 159 public void addType(ProfiledType pt) { 160 for (ProfiledType et : types) { 161 if (et.uri.equals(pt.uri)) { 162 if (pt.profiles != null) { 163 for (String p : pt.profiles) { 164 if (et.profiles == null) 165 et.profiles = new ArrayList<String>(); 166 if (!et.profiles.contains(p)) 167 et.profiles.add(p); 168 } 169 } 170 if (pt.bindings != null) { 171 for (ElementDefinitionBindingComponent b : pt.bindings) { 172 if (et.bindings == null) 173 et.bindings = new ArrayList<ElementDefinitionBindingComponent>(); 174 if (!et.hasBinding(b)) 175 et.bindings.add(b); 176 } 177 } 178 return; 179 } 180 } 181 types.add(pt); 182 } 183 184 public void addTypes(Collection<String> names) { 185 for (String n : names) 186 addType(new ProfiledType(n)); 187 } 188 189 public boolean hasType(IWorkerContext context, String... tn) { 190 for (String n : tn) { 191 String t = ProfiledType.ns(n); 192 if (typesContains(t)) 193 return true; 194 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", 195 "ClassInfo", "SimpleTypeInfo")) { 196 t = FP_NS + Utilities.capitalize(n); 197 if (typesContains(t)) 198 return true; 199 } 200 } 201 for (String n : tn) { 202 String id = n.contains("#") ? n.substring(0, n.indexOf("#")) : n; 203 String tail = null; 204 if (n.contains("#")) { 205 tail = n.substring(n.indexOf("#") + 1); 206 tail = tail.substring(tail.indexOf(".")); 207 } 208 String t = ProfiledType.ns(n); 209 StructureDefinition sd = context.fetchResource(StructureDefinition.class, t); 210 while (sd != null) { 211 if (tail == null && typesContains(sd.getUrl())) 212 return true; 213 if (tail == null && getSystemType(sd.getUrl()) != null && typesContains(getSystemType(sd.getUrl()))) 214 return true; 215 if (tail != null && typesContains(sd.getUrl() + "#" + sd.getType() + tail)) 216 return true; 217 if (sd.hasBaseDefinition()) { 218 if (sd.getType().equals("uri")) 219 sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string"); 220 else 221 sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 222 } else 223 sd = null; 224 } 225 } 226 return false; 227 } 228 229 private String getSystemType(String url) { 230 if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) { 231 String code = url.substring(40); 232 if (Utilities.existsInList(code, "string", "boolean", "integer", "decimal", "dateTime", "time", "Quantity")) 233 return FP_NS + Utilities.capitalize(code); 234 } 235 return null; 236 } 237 238 private boolean typesContains(String t) { 239 for (ProfiledType pt : types) 240 if (pt.uri.equals(t)) 241 return true; 242 return false; 243 } 244 245 public void update(TypeDetails source) { 246 for (ProfiledType pt : source.types) 247 addType(pt); 248 if (collectionStatus == null) 249 collectionStatus = source.collectionStatus; 250 else if (source.collectionStatus == CollectionStatus.UNORDERED) 251 collectionStatus = source.collectionStatus; 252 else 253 collectionStatus = CollectionStatus.ORDERED; 254 } 255 256 public TypeDetails union(TypeDetails right) { 257 TypeDetails result = new TypeDetails(null); 258 if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED) 259 result.collectionStatus = CollectionStatus.UNORDERED; 260 else 261 result.collectionStatus = CollectionStatus.ORDERED; 262 for (ProfiledType pt : types) 263 result.addType(pt); 264 for (ProfiledType pt : right.types) 265 result.addType(pt); 266 return result; 267 } 268 269 public TypeDetails intersect(TypeDetails right) { 270 TypeDetails result = new TypeDetails(null); 271 if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED) 272 result.collectionStatus = CollectionStatus.UNORDERED; 273 else 274 result.collectionStatus = CollectionStatus.ORDERED; 275 for (ProfiledType pt : types) { 276 boolean found = false; 277 for (ProfiledType r : right.types) 278 found = found || pt.uri.equals(r.uri); 279 if (found) 280 result.addType(pt); 281 } 282 for (ProfiledType pt : right.types) 283 result.addType(pt); 284 return result; 285 } 286 287 public boolean hasNoTypes() { 288 return types.isEmpty(); 289 } 290 291 public Set<String> getTypes() { 292 Set<String> res = new HashSet<String>(); 293 for (ProfiledType pt : types) 294 res.add(pt.uri); 295 return res; 296 } 297 298 public TypeDetails toSingleton() { 299 TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON); 300 result.types.addAll(types); 301 return result; 302 } 303 304 public CollectionStatus getCollectionStatus() { 305 return collectionStatus; 306 } 307 308 public boolean hasType(String n) { 309 String t = ProfiledType.ns(n); 310 if (typesContains(t)) 311 return true; 312 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "date", "dateTime", "time", 313 "ClassInfo", "SimpleTypeInfo")) { 314 t = FP_NS + Utilities.capitalize(n); 315 if (typesContains(t)) 316 return true; 317 } 318 return false; 319 } 320 321 public boolean hasType(Set<String> tn) { 322 for (String n : tn) { 323 String t = ProfiledType.ns(n); 324 if (typesContains(t)) 325 return true; 326 if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", 327 "ClassInfo", "SimpleTypeInfo")) { 328 t = FP_NS + Utilities.capitalize(n); 329 if (typesContains(t)) 330 return true; 331 } 332 } 333 return false; 334 } 335 336 public String describe() { 337 return getTypes().toString(); 338 } 339 340 public String getType() { 341 for (ProfiledType pt : types) 342 return pt.uri; 343 return null; 344 } 345 346 @Override 347 public String toString() { 348 return (collectionStatus == null ? collectionStatus.SINGLETON.toString() : collectionStatus.toString()) 349 + getTypes().toString(); 350 } 351 352 public String getTypeCode() throws DefinitionException { 353 if (types.size() != 1) 354 throw new DefinitionException("Multiple types? (" + types.toString() + ")"); 355 for (ProfiledType pt : types) 356 if (pt.uri.startsWith("http://hl7.org/fhir/StructureDefinition/")) 357 return pt.uri.substring(40); 358 else 359 return pt.uri; 360 return null; 361 } 362 363 public List<ProfiledType> getProfiledTypes() { 364 return types; 365 } 366 367 public boolean hasBinding() { 368 for (ProfiledType pt : types) { 369 if (pt.hasBindings()) 370 return true; 371 } 372 return false; 373 } 374 375 public ElementDefinitionBindingComponent getBinding() { 376 for (ProfiledType pt : types) { 377 for (ElementDefinitionBindingComponent b : pt.getBindings()) 378 return b; 379 } 380 return null; 381 } 382 383}