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.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 sd = context.fetchResource(StructureDefinition.class, url); 425 if (sd == null) 426 throw new DefinitionException("Unable to find type '"+t+"' for name '"+elementName+"' on property "+definition.getPath()); 427 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 428 } 429 } 430 List<Property> properties = new ArrayList<Property>(); 431 for (ElementDefinition child : children.getList()) { 432 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 433 } 434 profileUtilities.getCachedPropertyList().put(cacheKey, properties); 435 return properties; 436 } 437 438 private StructureDefinition findAncestor(String type, StructureDefinition sdt) { 439 if (sdt != null) { 440 StructureDefinition sd = context.fetchTypeDefinition(type); 441 StructureDefinition t = sd; 442 while (t != null) { 443 if (t == sdt) { 444 return sd; 445 } 446 t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); 447 } 448 } 449 return null; 450 } 451 452 453 private boolean isCDAElement(StructureDefinition sd) { 454 return sd.hasUrl() && sd.getUrl().startsWith(Constants.NS_CDA_ROOT); 455 } 456 457 458 protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { 459 ElementDefinition ed = definition; 460 StructureDefinition sd = structure; 461 SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); 462 if (children.getList().isEmpty()) { 463 // ok, find the right definitions 464 String t = null; 465 if (ed.getType().size() == 1) 466 t = ed.getType().get(0).getCode(); 467 else if (ed.getType().size() == 0) 468 throw new Error("types == 0, and no children found"); 469 else { 470 t = ed.getType().get(0).getCode(); 471 boolean all = true; 472 for (TypeRefComponent tr : ed.getType()) { 473 if (!tr.getCode().equals(t)) { 474 all = false; 475 break; 476 } 477 } 478 if (!all) { 479 // ok, it's polymorphic 480 t = type.getType(); 481 } 482 } 483 if (!"xhtml".equals(t)) { 484 sd = context.fetchResource(StructureDefinition.class, t); 485 if (sd == null) 486 throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath()); 487 children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 488 } 489 } 490 List<Property> properties = new ArrayList<Property>(); 491 for (ElementDefinition child : children.getList()) { 492 properties.add(new Property(context, child, sd, this.profileUtilities, this.utils)); 493 } 494 return properties; 495 } 496 497 private String tail(String path) { 498 return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; 499 } 500 501 public Property getChild(String elementName, String childName) throws FHIRException { 502 List<Property> children = getChildProperties(elementName, null); 503 for (Property p : children) { 504 if (p.getName().equals(childName)) { 505 return p; 506 } 507 } 508 return null; 509 } 510 511 public Property getChild(String name, TypeDetails type) throws DefinitionException { 512 List<Property> children = getChildProperties(type); 513 for (Property p : children) { 514 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 515 return p; 516 } 517 } 518 return null; 519 } 520 521 public Property getChild(String name) throws FHIRException { 522 List<Property> children = getChildProperties(name, null); 523 for (Property p : children) { 524 if (p.getName().equals(name)) { 525 return p; 526 } 527 } 528 return null; 529 } 530 531 public Property getChildSimpleName(String elementName, String name) throws FHIRException { 532 List<Property> children = getChildProperties(elementName, null); 533 for (Property p : children) { 534 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 535 return p; 536 } 537 } 538 return null; 539 } 540 541 public IWorkerContext getContext() { 542 return context; 543 } 544 545 @Override 546 public String toString() { 547 return definition.getPath(); 548 } 549 550 551 public boolean isJsonKeyArray() { 552 return definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY); 553 } 554 555 556 public String getJsonKeyProperty() { 557 return ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_PROP_KEY); 558 } 559 560 561 public boolean hasTypeSpecifier() { 562 return definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC); 563 } 564 565 566 public List<StringPair> getTypeSpecifiers() { 567 List<StringPair> res = new ArrayList<>(); 568 for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { 569 res.add(new StringPair(ToolingExtensions.readStringExtension(e, "condition"), ToolingExtensions.readStringExtension(e, "type"))); 570 } 571 return res; 572 } 573 574 575 public Property cloneToType(StructureDefinition sd) { 576 Property res = new Property(context, definition.copy(), sd); 577 res.definition.getType().clear(); 578 res.definition.getType().add(new TypeRefComponent(sd.getUrl())); 579 return res; 580 } 581 582 583 public boolean hasImpliedPrefix() { 584 return definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX); 585 } 586 587 588 public String getImpliedPrefix() { 589 return ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_IMPLIED_PREFIX); 590 } 591 592 593 public boolean isNullable() { 594 return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE); 595 } 596 597 598 public String summary() { 599 return structure.getUrl()+"#"+definition.getId(); 600 } 601 602 603 public boolean canBeEmpty() { 604 if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { 605 return !"absent".equals(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY)); 606 } else { 607 return false; 608 } 609 } 610 611 612 public boolean isLogical() { 613 return structure.getKind() == StructureDefinitionKind.LOGICAL; 614 } 615 616 617 public ProfileUtilities getUtils() { 618 return profileUtilities; 619 } 620 public ContextUtilities getContextUtils() { 621 return utils; 622 } 623 624 public boolean isJsonPrimitiveChoice() { 625 return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE); 626 } 627 628 public Object typeSummary() { 629 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" | "); 630 for (TypeRefComponent t : definition.getType()) { 631 b.append(t.getCode()); 632 } 633 return b.toString(); 634 } 635 636 637 public boolean hasJsonName() { 638 return definition.hasExtension(ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED); 639 } 640 641 642 public boolean isTranslatable() { 643 boolean ok = ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_TRANSLATABLE); 644 if (!ok && !definition.getPath().endsWith(".id") && !Utilities.existsInList(definition.getBase().getPath(), "Resource.id", "Reference.reference", "Coding.version", "Identifier.value", "SampledData.offsets", "SampledData.data", "ContactPoint.value")) { 645 String t = getType(); 646 ok = Utilities.existsInList(t, "string", "markdown"); 647 } 648 if (Utilities.existsInList(pathForElement(getStructure().getType(), getDefinition().getBase().getPath()), "CanonicalResource.version")) { 649 return false; 650 } 651 return ok; 652 } 653 654 655 private String pathForElement(String type, String path) { 656 // special case support for metadata elements prior to R5: 657 if (utils.getCanonicalResourceNames().contains(type)) { 658 String fp = path.replace(type+".", "CanonicalResource."); 659 if (Utilities.existsInList(fp, 660 "CanonicalResource.url", "CanonicalResource.identifier", "CanonicalResource.version", "CanonicalResource.name", 661 "CanonicalResource.title", "CanonicalResource.status", "CanonicalResource.experimental", "CanonicalResource.date", 662 "CanonicalResource.publisher", "CanonicalResource.contact", "CanonicalResource.description", "CanonicalResource.useContext", 663 "CanonicalResource.jurisdiction")) { 664 return fp; 665 } 666 } 667 return path; 668 } 669 670 public String getXmlTypeName() { 671 TypeRefComponent tr = type; 672 if (tr == null) { 673 tr = definition.getTypeFirstRep(); 674 } 675 StructureDefinition sd = context.fetchTypeDefinition(tr.getWorkingCode()); 676 return sd.getSnapshot().getElementFirstRep().getPath(); 677 } 678 679 680 public boolean isReference() { 681 if (type != null) { 682 return isRef(type); 683 } 684 for (TypeRefComponent tr : definition.getType()) { 685 boolean ref = isRef(tr); 686 if (ref) { 687 return true; 688 } 689 } 690 return false; 691 } 692 693 694 private boolean isRef(TypeRefComponent tr) { 695 return Utilities.existsInList(tr.getWorkingCode(), "Reference", "url", "uri", "canonical"); 696 } 697 698 699 public boolean canBeType(String type) { 700 for (TypeRefComponent tr : getDefinition().getType()) { 701 if (type.equals(tr.getWorkingCode())) { 702 return true; 703 } 704 } 705 return false; 706 } 707 708 709}