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