
001package org.hl7.fhir.r5.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 032 033 034import java.util.ArrayList; 035import java.util.List; 036 037import org.hl7.fhir.exceptions.DefinitionException; 038import org.hl7.fhir.exceptions.FHIRException; 039import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 040import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefinitions; 041import org.hl7.fhir.r5.context.ContextUtilities; 042import org.hl7.fhir.r5.context.IWorkerContext; 043import org.hl7.fhir.r5.extensions.ExtensionDefinitions; 044import org.hl7.fhir.r5.extensions.ExtensionUtilities; 045import org.hl7.fhir.r5.fhirpath.TypeDetails; 046import org.hl7.fhir.r5.formats.FormatUtilities; 047import org.hl7.fhir.r5.model.Constants; 048import org.hl7.fhir.r5.model.ElementDefinition; 049import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; 050import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 051import org.hl7.fhir.r5.model.Extension; 052import org.hl7.fhir.r5.model.StructureDefinition; 053import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 054 055import org.hl7.fhir.r5.utils.TypesUtilities; 056import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 057import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 058import org.hl7.fhir.utilities.StringPair; 059import org.hl7.fhir.utilities.Utilities; 060 061@MarkedToMoveToAdjunctPackage 062public class Property { 063 064 private IWorkerContext context; 065 private ElementDefinition definition; 066 private StructureDefinition structure; 067 private ProfileUtilities profileUtilities; 068 private ContextUtilities utils; 069 private TypeRefComponent type; 070 071 public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils) { 072 this.context = context; 073 this.definition = definition; 074 this.structure = structure; 075 this.utils = utils; 076 this.profileUtilities = profileUtilities; 077 } 078 079 080 public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils, String type) { 081 this.context = context; 082 this.definition = definition; 083 this.structure = structure; 084 this.profileUtilities = profileUtilities; 085 this.utils = utils; 086 for (TypeRefComponent tr : definition.getType()) { 087 if (tr.getWorkingCode().equals(type)) { 088 this.type = tr; 089 } 090 } 091 } 092 093 public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) { 094 this(context, definition, structure, new ProfileUtilities(context, null, null), new ContextUtilities(context)); 095 } 096 097 public String getName() { 098 return definition.getPath().substring(definition.getPath().lastIndexOf(".")+1); 099 } 100 101 public String getJsonName() { 102 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED)) { 103 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED); 104 } else { 105 return getName(); 106 } 107 } 108 109 public String getXmlName() { 110 if (definition.hasExtension(ExtensionDefinitions.EXT_XML_NAME)) { 111 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_XML_NAME); 112 } else if (definition.hasExtension(ExtensionDefinitions.EXT_XML_NAME_DEPRECATED)) { 113 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED); 114 } else { 115 return getName(); 116 } 117 } 118 119 public String getXmlNamespace() { 120 if (ExtensionUtilities.hasAnyOfExtensions(definition, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED)) { 121 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED); 122 } else if (ExtensionUtilities.hasAnyOfExtensions(structure, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED)) { 123 return ExtensionUtilities.readStringExtension(structure, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED); 124 } else { 125 return FormatUtilities.FHIR_NS; 126 } 127 } 128 129 public ElementDefinition getDefinition() { 130 return definition; 131 } 132 133 public String getType() { 134 if (type != null) { 135 return type.getWorkingCode(); 136 } else if (definition.getType().size() == 0) 137 return null; 138 else if (definition.getType().size() > 1) { 139 String tn = definition.getType().get(0).getWorkingCode(); 140 for (int i = 1; i < definition.getType().size(); i++) { 141 if (!tn.equals(definition.getType().get(i).getWorkingCode())) 142 return null; // though really, we shouldn't get here - type != null when definition.getType.size() > 1, or it should be 143 } 144 return tn; 145 } else 146 return definition.getType().get(0).getWorkingCode(); 147 } 148 149 public String getType(String elementName) { 150 if (type != null) { 151 return type.getWorkingCode(); 152 } 153 if (!definition.getPath().contains(".")) 154 return definition.getPath(); 155 ElementDefinition ed = definition; 156 if (definition.hasContentReference()) { 157 String url = null; 158 String path = definition.getContentReference(); 159 if (!path.startsWith("#")) { 160 if (path.contains("#")) { 161 url = path.substring(0, path.indexOf("#")); 162 path = path.substring(path.indexOf("#")+1); 163 } else { 164 throw new Error("Illegal content reference '"+path+"'"); 165 } 166 } else { 167 path = path.substring(1); 168 } 169 StructureDefinition sd = (url == null || url.equals(structure.getUrl())) ? structure : context.fetchResource(StructureDefinition.class, url, structure); 170 if (sd == null) { 171 throw new Error("Unknown Type in content reference '"+path+"'"); 172 } 173 boolean found = false; 174 for (ElementDefinition d : sd.getSnapshot().getElement()) { 175 if (d.hasId() && d.getId().equals(path)) { 176 found = true; 177 ed = d; 178 } 179 } 180 if (!found) 181 throw new Error("Unable to resolve "+definition.getContentReference()+" at "+definition.getPath()+" on "+sd.getUrl()); 182 } 183 if (ed.getType().size() == 0) 184 return null; 185 else if (ed.getType().size() > 1) { 186 String t = ed.getType().get(0).getCode(); 187 boolean all = true; 188 for (TypeRefComponent tr : ed.getType()) { 189 if (!t.equals(tr.getCode())) 190 all = false; 191 } 192 if (all) 193 return t; 194 String tail = ed.getPath().substring(ed.getPath().lastIndexOf(".")+1); 195 if (tail.endsWith("[x]") && elementName != null && elementName.startsWith(tail.substring(0, tail.length()-3))) { 196 String name = elementName.substring(tail.length()-3); 197 return isPrimitive(lowFirst(name)) ? lowFirst(name) : name; 198 } else { 199 if (ExtensionUtilities.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) 200 return ExtensionUtilities.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 201 throw new Error("logic error, gettype when types > 1, name mismatch for "+elementName+" on at "+ed.getPath()); 202 } 203 } else if (ed.getType().get(0).getCode() == null) { 204 if (Utilities.existsInList(ed.getId(), "Element.id", "Extension.url")) 205 return "string"; 206 else 207 return structure.getId(); 208 } else 209 return ed.getType().get(0).getWorkingCode(); 210 } 211 212 public boolean typeIsConsistent(String typeName) { 213 for (TypeRefComponent tr : definition.getType()) { 214 if (typeName.equals(tr.getWorkingCode()) || typeSpecializes(tr.getWorkingCode(), typeName)) { 215 return true; 216 } 217 } 218 return false; 219 } 220 221 222 private boolean typeSpecializes(String workingCode, String typeName) { 223 if ("string".equals(typeName)) { 224 return Utilities.existsInList(workingCode, "uri", "oid", "canonical", "url", "uuid", "id", "markdown"); 225 } 226 if ("integer".equals(typeName)) { 227 return Utilities.existsInList(workingCode, "positiveInt", "unsignedInt"); 228 } 229 return false; 230 } 231 232 233 public boolean hasType(String typeName) { 234 if (type != null) { 235 return false; // ? 236 } else if (definition.getType().size() == 0) { 237 return false; 238 } else if (isJsonPrimitiveChoice()) { 239 for (TypeRefComponent tr : definition.getType()) { 240 if (typeName.equals(tr.getWorkingCode())) { 241 return true; 242 } 243 } 244 return false; 245 } else if (definition.getType().size() > 1) { 246 String t = definition.getType().get(0).getCode(); 247 boolean all = true; 248 for (TypeRefComponent tr : definition.getType()) { 249 if (!t.equals(tr.getCode())) 250 all = false; 251 } 252 if (all) 253 return true; 254 String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".")+1); 255 if (tail.endsWith("[x]") && typeName.startsWith(tail.substring(0, tail.length()-3))) { 256// String name = elementName.substring(tail.length()-3); 257 return true; 258 } else 259 return false; 260 } else 261 return true; 262 } 263 264 public StructureDefinition getStructure() { 265 return structure; 266 } 267 268 /** 269 * Is the given name a primitive 270 * 271 * @param E.g. "Observation.status" 272 */ 273 public boolean isPrimitiveName(String name) { 274 String code = getType(name); 275 return isPrimitive(code); 276 } 277 278 /** 279 * Is the given type a primitive 280 * 281 * @param E.g. "integer" 282 */ 283 public boolean isPrimitive(String code) { 284 return context.isPrimitiveType(code); 285 } 286 287 public boolean isPrimitive() { 288 return isPrimitive(getType()); 289 } 290 private String lowFirst(String t) { 291 return t.substring(0, 1).toLowerCase()+t.substring(1); 292 } 293 294 public boolean isResource() { 295 if (type != null) { 296 String tc = type.getCode(); 297 return (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc)); 298 } else if (definition.getType().size() > 0) { 299 String tc = definition.getType().get(0).getCode(); 300 return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc)); 301 } 302 else { 303 return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); 304 } 305 } 306 307 public boolean isList() { 308 return !"1".equals(definition.getBase().hasMax() ? definition.getBase().getMax() : definition.getMax()); 309 } 310 311 /** 312 * This handles a very special case: An extension used with json extensions in CDS hooks, 313 * where the extension definition, not the base, decides whether it's an array or not 314 * @return 315 */ 316 public boolean isJsonList() { 317 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED)) { 318 return !"1".equals(definition.getMax()); 319 } else { 320 return !"1".equals(definition.getBase().hasMax() ? definition.getBase().getMax() : definition.getMax()); 321 } 322 } 323 324 public boolean isBaseList() { 325 return !"1".equals(definition.getBase().getMax()); 326 } 327 328 public String getScopedPropertyName() { 329 return definition.getBase().getPath(); 330 } 331 332 private boolean isElementWithOnlyExtension(final ElementDefinition ed, final List<ElementDefinition> children) { 333 boolean result = false; 334 if (!ed.getType().isEmpty()) { 335 result = true; 336 for (final ElementDefinition ele : children) { 337 if (!ele.getPath().contains("extension")) { 338 result = false; 339 break; 340 } 341 } 342 } 343 return result; 344 } 345 346 public boolean IsLogicalAndHasPrimitiveValue(String name) { 347// if (canBePrimitive!= null) 348// return canBePrimitive; 349 350 if (structure.getKind() != StructureDefinitionKind.LOGICAL) 351 return false; 352 if (!hasType(name)) 353 return false; 354 StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name)); 355 if (sd == null) 356 sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(getType(name), null)); 357 if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) 358 return true; 359 if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL) 360 return false; 361 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 362 if (ed.getPath().equals(sd.getId()+".value") && ed.getType().size() == 1 && isPrimitive(ed.getType().get(0).getCode())) { 363 return true; 364 } 365 } 366 return false; 367 } 368 369 public boolean isChoice() { 370 if (type != null) { 371 return true; 372 } 373 if (definition.getType().size() <= 1) 374 return false; 375 String tn = definition.getType().get(0).getCode(); 376 for (int i = 1; i < definition.getType().size(); i++) 377 if (!definition.getType().get(i).getCode().equals(tn)) 378 return true; 379 return false; 380 } 381 382 383 public List<Property> getChildProperties(String elementName, String statedType) throws FHIRException { 384 String cacheKey = structure.getVUrl()+"#"+definition.getPath()+":"+elementName+"/"+statedType; 385 List<Property> cached = profileUtilities.getCachedPropertyList().get(cacheKey); 386 if (cached != null) { 387 return cached; 388 } 389 ElementDefinition ed = definition; 390 StructureDefinition sd = structure; 391 boolean isCDA = isCDAElement(structure); 392 SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed, false); 393 String url = null; 394 if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) { 395 // ok, find the right definitions 396 String t = null; 397 if (ed.getType().size() == 1 && (statedType == null || !isCDA)) 398 t = ed.getType().get(0).getWorkingCode(); 399 else if (ed.getType().size() == 0) 400 throw new Error("types == 0, and no children found on "+getDefinition().getPath()); 401 else { 402 t = ed.getType().get(0).getWorkingCode(); 403 boolean all = true; 404 for (TypeRefComponent tr : ed.getType()) { 405 if (!tr.getWorkingCode().equals(t)) { 406 all = false; 407 break; 408 } 409 } 410 if (!all || (isCDA && statedType != null)) { 411 // ok, it's polymorphic 412 if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR) || isCDA) { 413 t = statedType; 414 if (t == null && ExtensionUtilities.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) 415 t = ExtensionUtilities.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 416 boolean ok = false; 417 for (TypeRefComponent tr : ed.getType()) { 418 if (tr.getWorkingCode().equals(t)) 419 ok = true; 420 if (Utilities.isAbsoluteUrl(tr.getWorkingCode())) { 421 StructureDefinition sdt = context.fetchResource(StructureDefinition.class, tr.getWorkingCode()); 422 if (sdt != null && sdt.getTypeTail().equals(t)) { 423 url = tr.getWorkingCode(); 424 ok = true; 425 } 426 if (!ok) { 427 sdt = findAncestor(t, sdt); 428 if (sdt != null) { 429 url = sdt.getUrl(); 430 ok = true; 431 } 432 } 433 } 434 if (ok) { 435 break; 436 } 437 } 438 if (!ok) { 439 throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath()); 440 } 441 } else { 442 t = elementName.substring(tail(ed.getPath()).length() - 3); 443 if (isPrimitive(lowFirst(t))) 444 t = lowFirst(t); 445 } 446 } 447 } 448 if (!"xhtml".equals(t)) { 449 for (TypeRefComponent aType: ed.getType()) { 450 if (aType.getWorkingCode().equals(t)) { 451 if (aType.hasProfile()) { 452 assert aType.getProfile().size() == 1; 453 url = aType.getProfile().get(0).getValue(); 454 } else { 455 url = ProfileUtilities.sdNs(t, null); 456 } 457 break; 458 } 459 } 460 if (url==null) { 461 throw new FHIRException("Unable to find type " + t + " for element " + elementName + " with path " + ed.getPath()); 462 } 463 sd = context.fetchResource(StructureDefinition.class, url); 464 if (sd == null) 465 throw new DefinitionException("Unable to find definition '"+url+"' for type '"+t+"' for name '"+elementName+"' on property "+definition.getPath()); 466 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0), false); 467 } 468 } 469 List<Property> properties = new ArrayList<Property>(); 470 for (ElementDefinition child : children.getList()) { 471 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 472 } 473 profileUtilities.getCachedPropertyList().put(cacheKey, properties); 474 return properties; 475 } 476 477 private StructureDefinition findAncestor(String type, StructureDefinition sdt) { 478 if (sdt != null) { 479 StructureDefinition sd = context.fetchTypeDefinition(type); 480 StructureDefinition t = sd; 481 while (t != null) { 482 if (t == sdt) { 483 return sd; 484 } 485 t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); 486 } 487 } 488 return null; 489 } 490 491 492 private boolean isCDAElement(StructureDefinition sd) { 493 return sd.hasUrl() && sd.getUrl().startsWith(Constants.NS_CDA_ROOT); 494 } 495 496 497 protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { 498 ElementDefinition ed = definition; 499 StructureDefinition sd = structure; 500 SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed, false); 501 if (children.getList().isEmpty()) { 502 // ok, find the right definitions 503 String t = null; 504 if (ed.getType().size() == 1) 505 t = ed.getType().get(0).getCode(); 506 else if (ed.getType().size() == 0) 507 throw new Error("types == 0, and no children found"); 508 else { 509 t = ed.getType().get(0).getCode(); 510 boolean all = true; 511 for (TypeRefComponent tr : ed.getType()) { 512 if (!tr.getCode().equals(t)) { 513 all = false; 514 break; 515 } 516 } 517 if (!all) { 518 // ok, it's polymorphic 519 t = type.getType(); 520 } 521 } 522 if (!"xhtml".equals(t)) { 523 sd = context.fetchResource(StructureDefinition.class, t); 524 if (sd == null) 525 throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath()); 526 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0), false); 527 } 528 } 529 List<Property> properties = new ArrayList<Property>(); 530 for (ElementDefinition child : children.getList()) { 531 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 532 } 533 return properties; 534 } 535 536 private String tail(String path) { 537 return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; 538 } 539 540 public Property getChild(String elementName, String childName) throws FHIRException { 541 List<Property> children = getChildProperties(elementName, null); 542 for (Property p : children) { 543 if (p.getName().equals(childName)) { 544 return p; 545 } 546 } 547 return null; 548 } 549 550 public Property getChild(String name, TypeDetails type) throws DefinitionException { 551 List<Property> children = getChildProperties(type); 552 for (Property p : children) { 553 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 554 return p; 555 } 556 } 557 return null; 558 } 559 560 public Property getChild(String name) throws FHIRException { 561 List<Property> children = getChildProperties(name, null); 562 for (Property p : children) { 563 if (p.getName().equals(name)) { 564 return p; 565 } 566 } 567 return null; 568 } 569 570 public Property getChildSimpleName(String elementName, String name) throws FHIRException { 571 List<Property> children = getChildProperties(elementName, null); 572 for (Property p : children) { 573 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 574 return p; 575 } 576 } 577 return null; 578 } 579 580 public IWorkerContext getContext() { 581 return context; 582 } 583 584 @Override 585 public String toString() { 586 return definition.getPath(); 587 } 588 589 590 public boolean isJsonKeyArray() { 591 return definition.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY); 592 } 593 594 595 public String getJsonKeyProperty() { 596 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_PROP_KEY); 597 } 598 599 600 public boolean hasTypeSpecifier() { 601 return definition.hasExtension(ExtensionDefinitions.EXT_TYPE_SPEC); 602 } 603 604 605 public List<StringPair> getTypeSpecifiers() { 606 List<StringPair> res = new ArrayList<>(); 607 for (Extension e : definition.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_SPEC)) { 608 res.add(new StringPair(ExtensionUtilities.readStringExtension(e, "condition"), ExtensionUtilities.readStringExtension(e, "type"))); 609 } 610 return res; 611 } 612 613 614 public Property cloneToType(StructureDefinition sd) { 615 Property res = new Property(context, definition.copy(), sd); 616 res.definition.getType().clear(); 617 res.definition.getType().add(new TypeRefComponent(sd.getUrl())); 618 return res; 619 } 620 621 622 public boolean hasImpliedPrefix() { 623 return definition.hasExtension(ExtensionDefinitions.EXT_IMPLIED_PREFIX); 624 } 625 626 627 public String getImpliedPrefix() { 628 return ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_IMPLIED_PREFIX); 629 } 630 631 632 public boolean isNullable() { 633 return ExtensionUtilities.readBoolExtension(definition, ExtensionDefinitions.EXT_JSON_NULLABLE); 634 } 635 636 637 public String summary() { 638 return structure.getUrl()+"#"+definition.getId(); 639 } 640 641 642 public boolean canBeEmpty() { 643 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_EMPTY)) { 644 return !"absent".equals(ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_EMPTY)); 645 } else { 646 return false; 647 } 648 } 649 650 651 public boolean isLogical() { 652 return structure.getKind() == StructureDefinitionKind.LOGICAL; 653 } 654 655 656 public ProfileUtilities getUtils() { 657 return profileUtilities; 658 } 659 public ContextUtilities getContextUtils() { 660 return utils; 661 } 662 663 public boolean isJsonPrimitiveChoice() { 664 return ExtensionUtilities.readBoolExtension(definition, ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE); 665 } 666 667 public Object typeSummary() { 668 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" | "); 669 for (TypeRefComponent t : definition.getType()) { 670 b.append(t.getCode()); 671 } 672 return b.toString(); 673 } 674 675 676 public boolean hasJsonName() { 677 return definition.hasExtension(ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED); 678 } 679 680 681 public boolean isTranslatable() { 682 boolean ok = ExtensionUtilities.readBoolExtension(definition, ExtensionDefinitions.EXT_TRANSLATABLE); 683 if (!ok && !definition.getPath().endsWith(".id") && !definition.getPath().endsWith(".linkId") && !Utilities.existsInList(definition.getBase().getPath(), "Resource.id", "Reference.reference", "Coding.version", "Identifier.value", "SampledData.offsets", "SampledData.data", "ContactPoint.value")) { 684 String t = getType(); 685 ok = Utilities.existsInList(t, "string", "markdown"); 686 } 687 if (Utilities.existsInList(pathForElement(getStructure().getType(), getDefinition().getBase().getPath()), "CanonicalResource.version")) { 688 return false; 689 } 690 return ok; 691 } 692 693 694 private String pathForElement(String type, String path) { 695 // special case support for metadata elements prior to R5: 696 if (utils.getCanonicalResourceNames().contains(type)) { 697 String fp = path.replace(type+".", "CanonicalResource."); 698 if (Utilities.existsInList(fp, 699 "CanonicalResource.url", "CanonicalResource.identifier", "CanonicalResource.version", "CanonicalResource.name", 700 "CanonicalResource.title", "CanonicalResource.status", "CanonicalResource.experimental", "CanonicalResource.date", 701 "CanonicalResource.publisher", "CanonicalResource.contact", "CanonicalResource.description", "CanonicalResource.useContext", 702 "CanonicalResource.jurisdiction")) { 703 return fp; 704 } 705 } 706 return path; 707 } 708 709 public String getXmlTypeName() { 710 TypeRefComponent tr = type; 711 if (tr == null) { 712 tr = definition.getTypeFirstRep(); 713 } 714 StructureDefinition sd = context.fetchTypeDefinition(tr.getWorkingCode()); 715 return sd.getSnapshot().getElementFirstRep().getPath(); 716 } 717 718 719 public boolean isReference() { 720 if (type != null) { 721 return isRef(type); 722 } 723 for (TypeRefComponent tr : definition.getType()) { 724 boolean ref = isRef(tr); 725 if (ref) { 726 return true; 727 } 728 } 729 return false; 730 } 731 732 733 private boolean isRef(TypeRefComponent tr) { 734 return Utilities.existsInList(tr.getWorkingCode(), "Reference", "url", "uri", "canonical"); 735 } 736 737 738 public boolean canBeType(String type) { 739 for (TypeRefComponent tr : getDefinition().getType()) { 740 if (type.equals(tr.getWorkingCode())) { 741 return true; 742 } 743 } 744 return false; 745 } 746 747 public String getExtensionStyle() { 748 ElementDefinition ed = getDefinition(); 749 if (ed.hasExtension(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED)) { 750 return ed.getExtensionString(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED); 751 } 752 if (ed.getType().size() == 1) { 753 StructureDefinition sd = context.fetchTypeDefinition(ed.getTypeFirstRep().getWorkingCode()); 754 if (sd != null && sd.hasExtension(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED)) { 755 return sd.getExtensionString(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED); 756 } 757 } 758 return null; 759 } 760}