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 hasType(String elementName) { 209 if (type != null) { 210 return false; // ? 211 } else if (definition.getType().size() == 0) { 212 return false; 213 } else if (isJsonPrimitiveChoice()) { 214 for (TypeRefComponent tr : definition.getType()) { 215 if (elementName.equals(tr.getWorkingCode())) { 216 return true; 217 } 218 } 219 return false; 220 } else if (definition.getType().size() > 1) { 221 String t = definition.getType().get(0).getCode(); 222 boolean all = true; 223 for (TypeRefComponent tr : definition.getType()) { 224 if (!t.equals(tr.getCode())) 225 all = false; 226 } 227 if (all) 228 return true; 229 String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".")+1); 230 if (tail.endsWith("[x]") && elementName.startsWith(tail.substring(0, tail.length()-3))) { 231// String name = elementName.substring(tail.length()-3); 232 return true; 233 } else 234 return false; 235 } else 236 return true; 237 } 238 239 public StructureDefinition getStructure() { 240 return structure; 241 } 242 243 /** 244 * Is the given name a primitive 245 * 246 * @param E.g. "Observation.status" 247 */ 248 public boolean isPrimitiveName(String name) { 249 String code = getType(name); 250 return isPrimitive(code); 251 } 252 253 /** 254 * Is the given type a primitive 255 * 256 * @param E.g. "integer" 257 */ 258 public boolean isPrimitive(String code) { 259 return context.isPrimitiveType(code); 260 } 261 262 public boolean isPrimitive() { 263 return isPrimitive(getType()); 264 } 265 private String lowFirst(String t) { 266 return t.substring(0, 1).toLowerCase()+t.substring(1); 267 } 268 269 public boolean isResource() { 270 if (type != null) { 271 String tc = type.getCode(); 272 return (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc)); 273 } else if (definition.getType().size() > 0) { 274 String tc = definition.getType().get(0).getCode(); 275 return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc)); 276 } 277 else { 278 return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); 279 } 280 } 281 282 public boolean isList() { 283 return !"1".equals(definition.getBase().hasMax() ? definition.getBase().getMax() : definition.getMax()); 284 } 285 286 public boolean isBaseList() { 287 return !"1".equals(definition.getBase().getMax()); 288 } 289 290 public String getScopedPropertyName() { 291 return definition.getBase().getPath(); 292 } 293 294 private boolean isElementWithOnlyExtension(final ElementDefinition ed, final List<ElementDefinition> children) { 295 boolean result = false; 296 if (!ed.getType().isEmpty()) { 297 result = true; 298 for (final ElementDefinition ele : children) { 299 if (!ele.getPath().contains("extension")) { 300 result = false; 301 break; 302 } 303 } 304 } 305 return result; 306 } 307 308 public boolean IsLogicalAndHasPrimitiveValue(String name) { 309// if (canBePrimitive!= null) 310// return canBePrimitive; 311 312 if (structure.getKind() != StructureDefinitionKind.LOGICAL) 313 return false; 314 if (!hasType(name)) 315 return false; 316 StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name)); 317 if (sd == null) 318 sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(getType(name), null)); 319 if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) 320 return true; 321 if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL) 322 return false; 323 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 324 if (ed.getPath().equals(sd.getId()+".value") && ed.getType().size() == 1 && isPrimitive(ed.getType().get(0).getCode())) { 325 return true; 326 } 327 } 328 return false; 329 } 330 331 public boolean isChoice() { 332 if (type != null) { 333 return true; 334 } 335 if (definition.getType().size() <= 1) 336 return false; 337 String tn = definition.getType().get(0).getCode(); 338 for (int i = 1; i < definition.getType().size(); i++) 339 if (!definition.getType().get(i).getCode().equals(tn)) 340 return true; 341 return false; 342 } 343 344 345 public List<Property> getChildProperties(String elementName, String statedType) throws FHIRException { 346 String cacheKey = structure.getVUrl()+"#"+definition.getPath()+":"+elementName+"/"+statedType; 347 List<Property> cached = profileUtilities.getCachedPropertyList().get(cacheKey); 348 if (cached != null) { 349 return cached; 350 } 351 ElementDefinition ed = definition; 352 StructureDefinition sd = structure; 353 boolean isCDA = isCDAElement(structure); 354 SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); 355 String url = null; 356 if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) { 357 // ok, find the right definitions 358 String t = null; 359 if (ed.getType().size() == 1 && (statedType == null || !isCDA)) 360 t = ed.getType().get(0).getWorkingCode(); 361 else if (ed.getType().size() == 0) 362 throw new Error("types == 0, and no children found on "+getDefinition().getPath()); 363 else { 364 t = ed.getType().get(0).getWorkingCode(); 365 boolean all = true; 366 for (TypeRefComponent tr : ed.getType()) { 367 if (!tr.getWorkingCode().equals(t)) { 368 all = false; 369 break; 370 } 371 } 372 if (!all || (isCDA && statedType != null)) { 373 // ok, it's polymorphic 374 if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR) || isCDA) { 375 t = statedType; 376 if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) 377 t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 378 boolean ok = false; 379 for (TypeRefComponent tr : ed.getType()) { 380 if (tr.getWorkingCode().equals(t)) 381 ok = true; 382 if (Utilities.isAbsoluteUrl(tr.getWorkingCode())) { 383 StructureDefinition sdt = context.fetchResource(StructureDefinition.class, tr.getWorkingCode()); 384 if (sdt != null && sdt.getTypeTail().equals(t)) { 385 url = tr.getWorkingCode(); 386 ok = true; 387 } 388 if (!ok) { 389 sdt = findAncestor(t, sdt); 390 if (sdt != null) { 391 url = sdt.getUrl(); 392 ok = true; 393 } 394 } 395 } 396 if (ok) { 397 break; 398 } 399 } 400 if (!ok) { 401 throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath()); 402 } 403 } else { 404 t = elementName.substring(tail(ed.getPath()).length() - 3); 405 if (isPrimitive(lowFirst(t))) 406 t = lowFirst(t); 407 } 408 } 409 } 410 if (!"xhtml".equals(t)) { 411 for (TypeRefComponent aType: ed.getType()) { 412 if (aType.getWorkingCode().equals(t)) { 413 if (aType.hasProfile()) { 414 assert aType.getProfile().size() == 1; 415 url = aType.getProfile().get(0).getValue(); 416 } else { 417 url = ProfileUtilities.sdNs(t, null); 418 } 419 break; 420 } 421 } 422 if (url==null) { 423 throw new FHIRException("Unable to find type " + t + " for element " + elementName + " with path " + ed.getPath()); 424 } 425 sd = context.fetchResource(StructureDefinition.class, url); 426 if (sd == null) 427 throw new DefinitionException("Unable to find type '"+t+"' for name '"+elementName+"' on property "+definition.getPath()); 428 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 429 } 430 } 431 List<Property> properties = new ArrayList<Property>(); 432 for (ElementDefinition child : children.getList()) { 433 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 434 } 435 profileUtilities.getCachedPropertyList().put(cacheKey, properties); 436 return properties; 437 } 438 439 private StructureDefinition findAncestor(String type, StructureDefinition sdt) { 440 if (sdt != null) { 441 StructureDefinition sd = context.fetchTypeDefinition(type); 442 StructureDefinition t = sd; 443 while (t != null) { 444 if (t == sdt) { 445 return sd; 446 } 447 t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); 448 } 449 } 450 return null; 451 } 452 453 454 private boolean isCDAElement(StructureDefinition sd) { 455 return sd.hasUrl() && sd.getUrl().startsWith(Constants.NS_CDA_ROOT); 456 } 457 458 459 protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { 460 ElementDefinition ed = definition; 461 StructureDefinition sd = structure; 462 SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); 463 if (children.getList().isEmpty()) { 464 // ok, find the right definitions 465 String t = null; 466 if (ed.getType().size() == 1) 467 t = ed.getType().get(0).getCode(); 468 else if (ed.getType().size() == 0) 469 throw new Error("types == 0, and no children found"); 470 else { 471 t = ed.getType().get(0).getCode(); 472 boolean all = true; 473 for (TypeRefComponent tr : ed.getType()) { 474 if (!tr.getCode().equals(t)) { 475 all = false; 476 break; 477 } 478 } 479 if (!all) { 480 // ok, it's polymorphic 481 t = type.getType(); 482 } 483 } 484 if (!"xhtml".equals(t)) { 485 sd = context.fetchResource(StructureDefinition.class, t); 486 if (sd == null) 487 throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath()); 488 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 489 } 490 } 491 List<Property> properties = new ArrayList<Property>(); 492 for (ElementDefinition child : children.getList()) { 493 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 494 } 495 return properties; 496 } 497 498 private String tail(String path) { 499 return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; 500 } 501 502 public Property getChild(String elementName, String childName) throws FHIRException { 503 List<Property> children = getChildProperties(elementName, null); 504 for (Property p : children) { 505 if (p.getName().equals(childName)) { 506 return p; 507 } 508 } 509 return null; 510 } 511 512 public Property getChild(String name, TypeDetails type) throws DefinitionException { 513 List<Property> children = getChildProperties(type); 514 for (Property p : children) { 515 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 516 return p; 517 } 518 } 519 return null; 520 } 521 522 public Property getChild(String name) throws FHIRException { 523 List<Property> children = getChildProperties(name, null); 524 for (Property p : children) { 525 if (p.getName().equals(name)) { 526 return p; 527 } 528 } 529 return null; 530 } 531 532 public Property getChildSimpleName(String elementName, String name) throws FHIRException { 533 List<Property> children = getChildProperties(elementName, null); 534 for (Property p : children) { 535 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 536 return p; 537 } 538 } 539 return null; 540 } 541 542 public IWorkerContext getContext() { 543 return context; 544 } 545 546 @Override 547 public String toString() { 548 return definition.getPath(); 549 } 550 551 552 public boolean isJsonKeyArray() { 553 return definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY); 554 } 555 556 557 public String getJsonKeyProperty() { 558 return ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_PROP_KEY); 559 } 560 561 562 public boolean hasTypeSpecifier() { 563 return definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC); 564 } 565 566 567 public List<StringPair> getTypeSpecifiers() { 568 List<StringPair> res = new ArrayList<>(); 569 for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { 570 res.add(new StringPair(ToolingExtensions.readStringExtension(e, "condition"), ToolingExtensions.readStringExtension(e, "type"))); 571 } 572 return res; 573 } 574 575 576 public Property cloneToType(StructureDefinition sd) { 577 Property res = new Property(context, definition.copy(), sd); 578 res.definition.getType().clear(); 579 res.definition.getType().add(new TypeRefComponent(sd.getUrl())); 580 return res; 581 } 582 583 584 public boolean hasImpliedPrefix() { 585 return definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX); 586 } 587 588 589 public String getImpliedPrefix() { 590 return ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_IMPLIED_PREFIX); 591 } 592 593 594 public boolean isNullable() { 595 return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE); 596 } 597 598 599 public String summary() { 600 return structure.getUrl()+"#"+definition.getId(); 601 } 602 603 604 public boolean canBeEmpty() { 605 if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { 606 return !"absent".equals(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY)); 607 } else { 608 return false; 609 } 610 } 611 612 613 public boolean isLogical() { 614 return structure.getKind() == StructureDefinitionKind.LOGICAL; 615 } 616 617 618 public ProfileUtilities getUtils() { 619 return profileUtilities; 620 } 621 public ContextUtilities getContextUtils() { 622 return utils; 623 } 624 625 public boolean isJsonPrimitiveChoice() { 626 return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE); 627 } 628 629 public Object typeSummary() { 630 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" | "); 631 for (TypeRefComponent t : definition.getType()) { 632 b.append(t.getCode()); 633 } 634 return b.toString(); 635 } 636 637 638 public boolean hasJsonName() { 639 return definition.hasExtension(ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED); 640 } 641 642 643 public boolean isTranslatable() { 644 boolean ok = ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_TRANSLATABLE); 645 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")) { 646 String t = getType(); 647 ok = Utilities.existsInList(t, "string", "markdown"); 648 } 649 if (Utilities.existsInList(pathForElement(getStructure().getType(), getDefinition().getBase().getPath()), "CanonicalResource.version")) { 650 return false; 651 } 652 return ok; 653 } 654 655 656 private String pathForElement(String type, String path) { 657 // special case support for metadata elements prior to R5: 658 if (utils.getCanonicalResourceNames().contains(type)) { 659 String fp = path.replace(type+".", "CanonicalResource."); 660 if (Utilities.existsInList(fp, 661 "CanonicalResource.url", "CanonicalResource.identifier", "CanonicalResource.version", "CanonicalResource.name", 662 "CanonicalResource.title", "CanonicalResource.status", "CanonicalResource.experimental", "CanonicalResource.date", 663 "CanonicalResource.publisher", "CanonicalResource.contact", "CanonicalResource.description", "CanonicalResource.useContext", 664 "CanonicalResource.jurisdiction")) { 665 return fp; 666 } 667 } 668 return path; 669 } 670 671 public String getXmlTypeName() { 672 TypeRefComponent tr = type; 673 if (tr == null) { 674 tr = definition.getTypeFirstRep(); 675 } 676 StructureDefinition sd = context.fetchTypeDefinition(tr.getWorkingCode()); 677 return sd.getSnapshot().getElementFirstRep().getPath(); 678 } 679 680 681 public boolean isReference() { 682 if (type != null) { 683 return isRef(type); 684 } 685 for (TypeRefComponent tr : definition.getType()) { 686 boolean ref = isRef(tr); 687 if (ref) { 688 return true; 689 } 690 } 691 return false; 692 } 693 694 695 private boolean isRef(TypeRefComponent tr) { 696 return Utilities.existsInList(tr.getWorkingCode(), "Reference", "url", "uri", "canonical"); 697 } 698 699 700 public boolean canBeType(String type) { 701 for (TypeRefComponent tr : getDefinition().getType()) { 702 if (type.equals(tr.getWorkingCode())) { 703 return true; 704 } 705 } 706 return false; 707 } 708 709 710}