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