
001package org.hl7.fhir.r4.elementmodel; 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.List; 034 035import org.hl7.fhir.exceptions.DefinitionException; 036import org.hl7.fhir.exceptions.FHIRException; 037import org.hl7.fhir.r4.conformance.ProfileUtilities; 038import org.hl7.fhir.r4.context.IWorkerContext; 039import org.hl7.fhir.r4.fhirpath.TypeDetails; 040import org.hl7.fhir.r4.formats.FormatUtilities; 041import org.hl7.fhir.r4.model.ElementDefinition; 042import org.hl7.fhir.r4.model.ElementDefinition.PropertyRepresentation; 043import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent; 044import org.hl7.fhir.r4.model.StructureDefinition; 045import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; 046import org.hl7.fhir.r4.utils.ToolingExtensions; 047import org.hl7.fhir.r4.utils.TypesUtilities; 048import org.hl7.fhir.utilities.Utilities; 049 050@Deprecated 051public class Property { 052 053 private IWorkerContext context; 054 private ElementDefinition definition; 055 private StructureDefinition structure; 056 private Boolean canBePrimitive; 057 058 public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) { 059 this.context = context; 060 this.definition = definition; 061 this.structure = structure; 062 } 063 064 public String getName() { 065 return definition.getPath().substring(definition.getPath().lastIndexOf(".") + 1); 066 } 067 068 public ElementDefinition getDefinition() { 069 return definition; 070 } 071 072 public String getType() { 073 if (definition.getType().size() == 0) 074 return null; 075 else if (definition.getType().size() > 1) { 076 String tn = definition.getType().get(0).getWorkingCode(); 077 for (int i = 1; i < definition.getType().size(); i++) { 078 if (!tn.equals(definition.getType().get(i).getWorkingCode())) 079 throw new Error("logic error, gettype when types > 1"); 080 } 081 return tn; 082 } else 083 return definition.getType().get(0).getWorkingCode(); 084 } 085 086 public String getType(String elementName) { 087 if (!definition.getPath().contains(".")) 088 return definition.getPath(); 089 ElementDefinition ed = definition; 090 if (definition.hasContentReference()) { 091 if (!definition.getContentReference().startsWith("#")) 092 throw new Error("not handled yet"); 093 boolean found = false; 094 for (ElementDefinition d : structure.getSnapshot().getElement()) { 095 if (d.hasId() && d.getId().equals(definition.getContentReference().substring(1))) { 096 found = true; 097 ed = d; 098 } 099 } 100 if (!found) 101 throw new Error("Unable to resolve " + definition.getContentReference() + " at " + definition.getPath() + " on " 102 + structure.getUrl()); 103 } 104 if (ed.getType().size() == 0) 105 return null; 106 else if (ed.getType().size() > 1) { 107 String t = ed.getType().get(0).getCode(); 108 boolean all = true; 109 for (TypeRefComponent tr : ed.getType()) { 110 if (!t.equals(tr.getCode())) 111 all = false; 112 } 113 if (all) 114 return t; 115 String tail = ed.getPath().substring(ed.getPath().lastIndexOf(".") + 1); 116 if (tail.endsWith("[x]") && elementName != null && elementName.startsWith(tail.substring(0, tail.length() - 3))) { 117 String name = elementName.substring(tail.length() - 3); 118 return isPrimitive(lowFirst(name)) ? lowFirst(name) : name; 119 } else 120 throw new Error( 121 "logic error, gettype when types > 1, name mismatch for " + elementName + " on at " + ed.getPath()); 122 } else if (ed.getType().get(0).getCode() == null) { 123 if (Utilities.existsInList(ed.getId(), "Element.id", "Extension.url")) 124 return "string"; 125 else 126 return structure.getId(); 127 } else 128 return ed.getType().get(0).getWorkingCode(); 129 } 130 131 public boolean hasType(String elementName) { 132 if (definition.getType().size() == 0) 133 return false; 134 else if (definition.getType().size() > 1) { 135 String t = definition.getType().get(0).getCode(); 136 boolean all = true; 137 for (TypeRefComponent tr : definition.getType()) { 138 if (!t.equals(tr.getCode())) 139 all = false; 140 } 141 if (all) 142 return true; 143 String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".") + 1); 144 if (tail.endsWith("[x]") && elementName.startsWith(tail.substring(0, tail.length() - 3))) { 145 String name = elementName.substring(tail.length() - 3); 146 return true; 147 } else 148 return false; 149 } else 150 return true; 151 } 152 153 public StructureDefinition getStructure() { 154 return structure; 155 } 156 157 /** 158 * Is the given name a primitive 159 * 160 * @param E.g. "Observation.status" 161 */ 162 public boolean isPrimitiveName(String name) { 163 String code = getType(name); 164 return isPrimitive(code); 165 } 166 167 /** 168 * Is the given type a primitive 169 * 170 * @param E.g. "integer" 171 */ 172 public boolean isPrimitive(String code) { 173 return TypesUtilities.isPrimitive(code); 174 // was this... but this can be very inefficient compared to hard coding the list 175// StructureDefinition sd = context.fetchTypeDefinition(code); 176// return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; 177 } 178 179 private String lowFirst(String t) { 180 return t.substring(0, 1).toLowerCase() + t.substring(1); 181 } 182 183 public boolean isResource() { 184 if (definition.getType().size() > 0) 185 return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) 186 || "DomainResource".equals(definition.getType().get(0).getCode())); 187 else 188 return !definition.getPath().contains(".") && structure.getKind() == StructureDefinitionKind.RESOURCE; 189 } 190 191 public boolean isList() { 192 return !"1".equals(definition.getMax()); 193 } 194 195 public String getScopedPropertyName() { 196 return definition.getBase().getPath(); 197 } 198 199 public String getNamespace() { 200 if (ToolingExtensions.hasExtension(definition, 201 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 202 return ToolingExtensions.readStringExtension(definition, 203 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 204 if (ToolingExtensions.hasExtension(structure, 205 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 206 return ToolingExtensions.readStringExtension(structure, 207 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 208 return FormatUtilities.FHIR_NS; 209 } 210 211 private boolean isElementWithOnlyExtension(final ElementDefinition ed, final List<ElementDefinition> children) { 212 boolean result = false; 213 if (!ed.getType().isEmpty()) { 214 result = true; 215 for (final ElementDefinition ele : children) { 216 if (!ele.getPath().contains("extension")) { 217 result = false; 218 break; 219 } 220 } 221 } 222 return result; 223 } 224 225 public boolean IsLogicalAndHasPrimitiveValue(String name) { 226// if (canBePrimitive!= null) 227// return canBePrimitive; 228 229 canBePrimitive = false; 230 if (structure.getKind() != StructureDefinitionKind.LOGICAL) 231 return false; 232 if (!hasType(name)) 233 return false; 234 StructureDefinition sd = context.fetchResource(StructureDefinition.class, 235 structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/") + 1) + getType(name)); 236 if (sd == null) 237 sd = context.fetchResource(StructureDefinition.class, 238 ProfileUtilities.sdNs(getType(name), context.getOverrideVersionNs())); 239 if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) 240 return true; 241 if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL) 242 return false; 243 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 244 if (ed.getPath().equals(sd.getId() + ".value") && ed.getType().size() == 1 245 && isPrimitive(ed.getType().get(0).getCode())) { 246 canBePrimitive = true; 247 return true; 248 } 249 } 250 return false; 251 } 252 253 public boolean isChoice() { 254 if (definition.getType().size() <= 1) 255 return false; 256 String tn = definition.getType().get(0).getCode(); 257 for (int i = 1; i < definition.getType().size(); i++) 258 if (!definition.getType().get(i).getCode().equals(tn)) 259 return true; 260 return false; 261 } 262 263 protected List<Property> getChildProperties(String elementName, String statedType) throws FHIRException { 264 ElementDefinition ed = definition; 265 StructureDefinition sd = structure; 266 List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); 267 String url = null; 268 if (children.isEmpty() || isElementWithOnlyExtension(ed, children)) { 269 // ok, find the right definitions 270 String t = null; 271 if (ed.getType().size() == 1) 272 t = ed.getType().get(0).getWorkingCode(); 273 else if (ed.getType().size() == 0) 274 throw new Error("types == 0, and no children found on " + getDefinition().getPath()); 275 else { 276 t = ed.getType().get(0).getWorkingCode(); 277 boolean all = true; 278 for (TypeRefComponent tr : ed.getType()) { 279 if (!tr.getWorkingCode().equals(t)) { 280 all = false; 281 break; 282 } 283 } 284 if (!all) { 285 // ok, it's polymorphic 286 if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) { 287 t = statedType; 288 if (t == null && ToolingExtensions.hasExtension(ed, 289 "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) 290 t = ToolingExtensions.readStringExtension(ed, 291 "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 292 boolean ok = false; 293 for (TypeRefComponent tr : ed.getType()) { 294 if (tr.getWorkingCode().equals(t)) 295 ok = true; 296 if (Utilities.isAbsoluteUrl(tr.getWorkingCode())) { 297 StructureDefinition sdt = context.fetchResource(StructureDefinition.class, tr.getWorkingCode()); 298 if (sdt != null && sdt.getType().equals(t)) { 299 url = tr.getWorkingCode(); 300 ok = true; 301 } 302 } 303 if (ok) 304 break; 305 } 306 if (!ok) 307 throw new DefinitionException("Type '" + t + "' is not an acceptable type for '" + elementName 308 + "' on property " + definition.getPath()); 309 310 } else { 311 t = elementName.substring(tail(ed.getPath()).length() - 3); 312 if (isPrimitive(lowFirst(t))) 313 t = lowFirst(t); 314 } 315 } 316 } 317 if (!"xhtml".equals(t)) { 318 for (TypeRefComponent aType : ed.getType()) { 319 if (aType.getWorkingCode().equals(t)) { 320 if (aType.hasProfile()) { 321 assert aType.getProfile().size() == 1; 322 url = aType.getProfile().get(0).getValue(); 323 } else { 324 url = ProfileUtilities.sdNs(t, context.getOverrideVersionNs()); 325 } 326 break; 327 } 328 } 329 if (url == null) 330 throw new FHIRException( 331 "Unable to find type " + t + " for element " + elementName + " with path " + ed.getPath()); 332 sd = context.fetchResource(StructureDefinition.class, url); 333 if (sd == null) 334 throw new DefinitionException( 335 "Unable to find type '" + t + "' for name '" + elementName + "' on property " + definition.getPath()); 336 children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 337 } 338 } 339 List<Property> properties = new ArrayList<Property>(); 340 for (ElementDefinition child : children) { 341 properties.add(new Property(context, child, sd)); 342 } 343 return properties; 344 } 345 346 protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { 347 ElementDefinition ed = definition; 348 StructureDefinition sd = structure; 349 List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); 350 if (children.isEmpty()) { 351 // ok, find the right definitions 352 String t = null; 353 if (ed.getType().size() == 1) 354 t = ed.getType().get(0).getCode(); 355 else if (ed.getType().size() == 0) 356 throw new Error("types == 0, and no children found"); 357 else { 358 t = ed.getType().get(0).getCode(); 359 boolean all = true; 360 for (TypeRefComponent tr : ed.getType()) { 361 if (!tr.getCode().equals(t)) { 362 all = false; 363 break; 364 } 365 } 366 if (!all) { 367 // ok, it's polymorphic 368 t = type.getType(); 369 } 370 } 371 if (!"xhtml".equals(t)) { 372 sd = context.fetchResource(StructureDefinition.class, t); 373 if (sd == null) 374 throw new DefinitionException( 375 "Unable to find class '" + t + "' for name '" + ed.getPath() + "' on property " + definition.getPath()); 376 children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 377 } 378 } 379 List<Property> properties = new ArrayList<Property>(); 380 for (ElementDefinition child : children) { 381 properties.add(new Property(context, child, sd)); 382 } 383 return properties; 384 } 385 386 private String tail(String path) { 387 return path.contains(".") ? path.substring(path.lastIndexOf(".") + 1) : path; 388 } 389 390 public Property getChild(String elementName, String childName) throws FHIRException { 391 List<Property> children = getChildProperties(elementName, null); 392 for (Property p : children) { 393 if (p.getName().equals(childName)) { 394 return p; 395 } 396 } 397 return null; 398 } 399 400 public Property getChild(String name, TypeDetails type) throws DefinitionException { 401 List<Property> children = getChildProperties(type); 402 for (Property p : children) { 403 if (p.getName().equals(name) || p.getName().equals(name + "[x]")) { 404 return p; 405 } 406 } 407 return null; 408 } 409 410 public Property getChild(String name) throws FHIRException { 411 List<Property> children = getChildProperties(name, null); 412 for (Property p : children) { 413 if (p.getName().equals(name)) { 414 return p; 415 } 416 } 417 return null; 418 } 419 420 public Property getChildSimpleName(String elementName, String name) throws FHIRException { 421 List<Property> children = getChildProperties(elementName, null); 422 for (Property p : children) { 423 if (p.getName().equals(name) || p.getName().equals(name + "[x]")) { 424 return p; 425 } 426 } 427 return null; 428 } 429 430 public IWorkerContext getContext() { 431 return context; 432 } 433 434 @Override 435 public String toString() { 436 return definition.getPath(); 437 } 438 439}