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