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