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