
001package org.hl7.fhir.r5.elementmodel; 002 003import java.io.IOException; 004import java.io.PrintStream; 005 006/* 007 Copyright (c) 2011+, HL7, Inc. 008 All rights reserved. 009 010 Redistribution and use in source and binary forms, with or without modification, 011 are permitted provided that the following conditions are met: 012 013 * Redistributions of source code must retain the above copyright notice, this 014 list of conditions and the following disclaimer. 015 * Redistributions in binary form must reproduce the above copyright notice, 016 this list of conditions and the following disclaimer in the documentation 017 and/or other materials provided with the distribution. 018 * Neither the name of HL7 nor the names of its contributors may be used to 019 endorse or promote products derived from this software without specific 020 prior written permission. 021 022 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 023 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 024 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 025 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 026 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 027 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 028 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 029 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 030 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 031 POSSIBILITY OF SUCH DAMAGE. 032 033 */ 034import java.util.ArrayList; 035import java.util.Arrays; 036import java.util.Comparator; 037import java.util.HashMap; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Map; 041import java.util.Set; 042 043import org.apache.commons.lang3.Validate; 044import org.hl7.fhir.exceptions.FHIRException; 045import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 046import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; 047import org.hl7.fhir.r5.extensions.ExtensionsUtils; 048import org.hl7.fhir.r5.model.Base; 049import org.hl7.fhir.r5.model.DataType; 050import org.hl7.fhir.r5.model.ElementDefinition; 051import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 052import org.hl7.fhir.r5.model.Enumerations.BindingStrength; 053import org.hl7.fhir.r5.model.ICoding; 054import org.hl7.fhir.r5.model.StringType; 055import org.hl7.fhir.r5.model.StructureDefinition; 056import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 057import org.hl7.fhir.r5.model.TypeConvertor; 058import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 059import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 060import org.hl7.fhir.r5.utils.ToolingExtensions; 061import org.hl7.fhir.r5.utils.UserDataNames; 062import org.hl7.fhir.utilities.ElementDecoration; 063import org.hl7.fhir.utilities.ElementDecoration.DecorationType; 064import org.hl7.fhir.utilities.FhirPublication; 065import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 066import org.hl7.fhir.utilities.NamedItemList; 067import org.hl7.fhir.utilities.NamedItemList.NamedItem; 068import org.hl7.fhir.utilities.SourceLocation; 069import org.hl7.fhir.utilities.Utilities; 070import org.hl7.fhir.utilities.validation.ValidationMessage; 071import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 072import org.hl7.fhir.utilities.xhtml.XhtmlNode; 073 074/** 075 * This class represents the underlying reference model of FHIR 076 * 077 * A resource is nothing but a set of elements, where every element has a 078 * name, maybe a stated type, maybe an id, and either a value or child elements 079 * (one or the other, but not both or neither) 080 * 081 * @author Grahame Grieve 082 * 083 */ 084@MarkedToMoveToAdjunctPackage 085public class Element extends Base implements NamedItem { 086 public class SliceDefinition { 087 088 private StructureDefinition profile; 089 private ElementDefinition definition; 090 private ElementDefinition slice; 091 092 public SliceDefinition(StructureDefinition profile, ElementDefinition definition, ElementDefinition slice) { 093 this.profile = profile; 094 this.definition = definition; 095 this.slice = slice; 096 } 097 098 public StructureDefinition getProfile() { 099 return profile; 100 } 101 102 public ElementDefinition getDefinition() { 103 return definition; 104 } 105 106 public ElementDefinition getSlice() { 107 return slice; 108 } 109 110 } 111 112 private static final HashSet<String> extensionList = new HashSet<>(Arrays.asList("extension", "modifierExtension")); 113 114 public enum SpecialElement { 115 CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, BUNDLE_ISSUES, PARAMETER, LOGICAL; 116 117 public static SpecialElement fromProperty(Property property) { 118 if (property.getStructure().getType().equals("Parameters")) 119 return PARAMETER; 120 if (property.getStructure().getType().equals("Bundle") && property.getName().equals("resource")) 121 return BUNDLE_ENTRY; 122 if (property.getStructure().getType().equals("Bundle") && property.getName().equals("outcome")) 123 return BUNDLE_OUTCOME; 124 if (property.getStructure().getType().equals("Bundle") && property.getName().equals("issues")) 125 return BUNDLE_ISSUES; 126 if (property.getName().equals("contained")) 127 return CONTAINED; 128 if (property.getStructure().getKind() == StructureDefinitionKind.LOGICAL) 129 return LOGICAL; 130 throw new FHIRException("Unknown resource containing a native resource: "+property.getDefinition().getId()); 131 } 132 133 public String toHuman() { 134 switch (this) { 135 case BUNDLE_ENTRY: return "entry"; 136 case BUNDLE_OUTCOME: return "outcome"; 137 case BUNDLE_ISSUES: return "issues"; 138 case CONTAINED: return "contained"; 139 case PARAMETER: return "parameter"; 140 case LOGICAL: return "logical"; 141 default: return "??"; 142 } 143 } 144 } 145 146 private List<String> comments;// not relevant for production, but useful in documentation 147 private String name; 148 private String type; 149 private String value; 150 private int index = -1; 151 private NamedItemList<Element> children; 152 private Property property; 153 private Property elementProperty; // this is used when special is set to true - it tracks the underlying element property which is used in a few places 154 private int line; 155 private int col; 156 private SpecialElement special; 157 private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation 158 private String explicitType; // for xsi:type attribute 159 private Element parentForValidator; 160 private boolean hasParentForValidator; 161 private String path; 162 private List<ValidationMessage> messages; 163 private boolean prohibited; 164 private boolean required; 165 private int descendentCount; 166 private int instanceId; 167 private boolean isNull; 168 private Base source; 169 private boolean ignorePropertyOrder; 170 private FhirFormat format; 171 private Object nativeObject; 172 private List<SliceDefinition> sliceDefinitions; 173 private boolean elided; 174 175 public Element(String name) { 176 super(); 177 this.name = name; 178 } 179 180 public Element(Element other) { 181 super(); 182 name = other.name; 183 type = other.type; 184 property = other.property; 185 elementProperty = other.elementProperty; 186 special = other.special; 187 } 188 189 public Element(String name, Property property) { 190 super(); 191 this.name = name; 192 this.property = property; 193 if (property.isResource()) { 194 children = new NamedItemList<>(); 195 } 196 } 197 198 public Element(String name, Property property, String type, String value) { 199 super(); 200 this.name = name; 201 this.property = property; 202 this.type = type; 203 this.value = value; 204 } 205 206 public void updateProperty(Property property, SpecialElement special, Property elementProperty) { 207 this.property = property; 208 this.elementProperty = elementProperty; 209 this.special = special; 210 } 211 212 public SpecialElement getSpecial() { 213 return special; 214 } 215 216 public String getName() { 217 return name; 218 } 219 220 public String getJsonName() { 221 return property.getJsonName(); 222 } 223 224 public String getType() { 225 if (type == null) 226 return property.getType(name); 227 else 228 return type; 229 } 230 231 public String getValue() { 232 if (xhtml != null) { 233 throw new Error("Don't use getValue() for xhtml"); 234 } 235 return value; 236 } 237 238 public boolean hasChildren() { 239 return !(children == null || children.isEmpty()); 240 } 241 242 public NamedItemList<Element> getChildren() { 243 if (children == null) 244 children = new NamedItemList<Element>(); 245 return children; 246 } 247 248 public boolean hasComments() { 249 return !(comments == null || comments.isEmpty()); 250 } 251 252 public List<String> getComments() { 253 if (comments == null) 254 comments = new ArrayList<String>(); 255 return comments; 256 } 257 258 public Property getProperty() { 259 return property; 260 } 261 262 public void setValue(String value) { 263 this.value = value; 264 } 265 266 public Element setType(String type) { 267 this.type = type; 268 return this; 269 270 } 271 272 public boolean isNull() { 273 return isNull; 274 } 275 276 public void setNull(boolean isNull) { 277 this.isNull = isNull; 278 } 279 280 public boolean hasValue() { 281 return value != null; 282 } 283 284 public List<Element> getChildrenByName(String name) { 285 return children.getByName(name); 286 } 287 288 public void numberChildren() { 289 if (children == null) 290 return; 291 292 String last = ""; 293 int index = 0; 294 for (Element child : children) { 295 if (child.getProperty().isList()) { 296 if (last.equals(child.getName())) { 297 index++; 298 } else { 299 last = child.getName(); 300 index = 0; 301 } 302 child.index = index; 303 } else { 304 child.index = -1; 305 } 306 child.numberChildren(); 307 } 308 } 309 310 public int getIndex() { 311 return index; 312 } 313 314 public boolean hasIndex() { 315 return index > -1; 316 } 317 318 public void setIndex(int index) { 319 this.index = index; 320 } 321 322 public String getChildValue(String name) { 323 if (children == null) 324 return null; 325 for (Element child : children) { 326 if (name.equals(child.getName())) 327 return child.getValue(); 328 } 329 for (Element child : children) { 330 if (name.equals(child.getNameBase())) 331 return child.getValue(); 332 } 333 return null; 334 } 335 336 private String getNameBase() { 337 if (property.isChoice()) { 338 return property.getName().replace("[x]", ""); 339 } else { 340 return getName(); 341 } 342 } 343 344 public void setChildValue(String name, String value) { 345 if (children == null) 346 children = new NamedItemList<Element>(); 347 for (Element child : children) { 348 if (name.equals(child.getName())) { 349 if (!child.isPrimitive()) 350 throw new Error("Cannot set a value of a non-primitive type ("+name+" on "+this.getName()+")"); 351 child.setValue(value); 352 } 353 } 354 355 try { 356 setProperty(name.hashCode(), name, new StringType(value)); 357 } catch (FHIRException e) { 358 throw new Error(e); 359 } 360 } 361 362 public void setChildValue(String name, Base value) { 363 if (children == null) 364 children = new NamedItemList<Element>(); 365 for (Element child : children) { 366 if (nameMatches(child.getName(), name)) { 367 if (!child.isPrimitive()) 368 throw new Error("Cannot set a value of a non-primitive type ("+name+" on "+this.getName()+")"); 369 child.setValue(value.primitiveValue()); 370 return; 371 } 372 } 373 374 try { 375 setProperty(name.hashCode(), name, value); 376 } catch (FHIRException e) { 377 throw new Error(e); 378 } 379 } 380 381 public List<Element> getChildren(String name) { 382 List<Element> res = new ArrayList<Element>(); 383 if (children != null) { 384 if (children.size() > 20) { 385 List<Element> l = children.getByName(name); 386 if (l != null) { 387 res.addAll(l); 388 } 389 } else { 390 for (Element child : children) { 391 if (name.equals(child.getName())) 392 res.add(child); 393 } 394 } 395 } 396 return res; 397 } 398 399 public boolean hasType() { 400 if (type == null) 401 return property.hasType(name); 402 else 403 return true; 404 } 405 406 @Override 407 public String fhirType() { 408 return getType(); 409 } 410 411 @Override 412 public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { 413 if (isPrimitive() && (hash == "value".hashCode()) && !Utilities.noString(value)) { 414// String tn = getType(); 415// throw new Error(tn+" not done yet"); 416 Base[] b = new Base[1]; 417 b[0] = new StringType(value); 418 return b; 419 } 420 421 List<Base> result = new ArrayList<Base>(); 422 if (children != null) { 423 if (children.size() > 20) { 424 List<Element> l = children.getByName(name); 425 if (l != null) { 426 result.addAll(l); 427 } 428 } else { 429 for (Element child : children) { 430 if (child.getName().equals(name)) { 431 result.add(child); 432 } 433 if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]")) { 434 result.add(child); 435 } 436 } 437 } 438 } 439 if (result.isEmpty() && checkValid) { 440// throw new FHIRException("not determined yet"); 441 } 442 return result.toArray(new Base[result.size()]); 443 } 444 445 @Override 446 protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) { 447 if (children != null) { 448 Map<String, org.hl7.fhir.r5.model.Property> map = new HashMap<String, org.hl7.fhir.r5.model.Property>(); 449 for (Element c : children) { 450 org.hl7.fhir.r5.model.Property p = map.get(c.getName()); 451 if (p == null) { 452 p = new org.hl7.fhir.r5.model.Property(c.getName(), c.fhirType(), c.getProperty().getDefinition().getDefinition(), c.getProperty().getDefinition().getMin(), maxToInt(c.getProperty().getDefinition().getMax()), c); 453 childProps.add(p); 454 map.put(c.getName(), p); 455 456 } else 457 p.getValues().add(c); 458 } 459 } 460 } 461 462 @Override 463 public Base setProperty(int hash, String name, Base value) throws FHIRException { 464 if ("xhtml".equals(getType()) && (hash == "value".hashCode())) { 465 this.xhtml = TypeConvertor.castToXhtml(value); 466 this.value = TypeConvertor.castToXhtmlString(value); 467 return this; 468 } 469 if (isPrimitive() && (hash == "value".hashCode())) { 470 this.value = TypeConvertor.castToString(value).asStringValue(); 471 return this; 472 } 473 474 if (!value.isPrimitive() && !(value instanceof Element)) { 475 if (isDataType(value)) 476 value = convertToElement(property.getChild(name), value); 477 else 478 throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type"); 479 } 480 481 if (children == null) 482 children = new NamedItemList<Element>(); 483 Element childForValue = null; 484 485 // look through existing children 486 for (Element child : children) { 487 if (child.getName().equals(name)) { 488 if (!child.isList()) { 489 childForValue = child; 490 break; 491 } else { 492 Element ne = new Element(child).setFormat(format); 493 children.add(ne); 494 ne.index = children.getSizeByName(ne.getListName()) - 1; 495 childForValue = ne; 496 break; 497 } 498 } 499 } 500 501 int i = 0; 502 if (childForValue == null) { 503 for (Property p : property.getChildProperties(this.name, type)) { 504 int t = -1; 505 for (int c =0; c < children.size(); c++) { 506 Element e = children.get(c); 507 if (p.getName().equals(e.getName())) 508 t = c; 509 } 510 if (t >= i) 511 i = t+1; 512 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 513 Element ne = new Element(name, p).setFormat(format); 514 children.add(i, ne); 515 childForValue = ne; 516 break; 517 } else if (p.getName().endsWith("[x]") && name.startsWith(p.getName().replace("[x]", ""))) { 518 Element ne = new Element(p.getName(), p).setFormat(format); 519 children.add(i, ne); 520 childForValue = ne; 521 break; 522 } 523 } 524 } 525 526 if (childForValue == null) 527 throw new Error("Cannot set property "+name+" on "+this.name); 528 else if (value.isPrimitive()) { 529 if (childForValue.property.getName().endsWith("[x]")) 530 childForValue.name = childForValue.name.replace("[x]", "")+Utilities.capitalize(value.fhirType()); 531 childForValue.setValue(value.primitiveValue()); 532 } else { 533 Element ve = (Element) value; 534 childForValue.type = ve.getType(); 535 if (childForValue.property.getName().endsWith("[x]")) 536 childForValue.name = name+Utilities.capitalize(childForValue.type); 537 else if (value.isResource()) { 538 if (childForValue.elementProperty == null) 539 childForValue.elementProperty = childForValue.property; 540 childForValue.property = ve.property; 541 childForValue.special = SpecialElement.BUNDLE_ENTRY; 542 } 543 if (ve.children != null) { 544 if (childForValue.children == null) 545 childForValue.children = new NamedItemList<Element>(); 546 else 547 childForValue.children.clear(); 548 childForValue.children.addAll(ve.children); 549 } 550 } 551 return childForValue; 552 } 553 554 private Base convertToElement(Property prop, Base v) throws FHIRException { 555 return new ObjectConverter(property.getContext()).convert(prop, (DataType) v); 556 } 557 558 private boolean isDataType(Base v) { 559 return v instanceof DataType && property.getContextUtils().getTypeNames().contains(v.fhirType()); 560 } 561 562 @Override 563 public Base makeProperty(int hash, String name) throws FHIRException { 564 if (isPrimitive() && (hash == "value".hashCode())) { 565 return new StringType(value); 566 } else { 567 return makeElement(name); 568 } 569 } 570 571 public Element makeElement(String name) throws FHIRException { 572 if (children == null) 573 children = new NamedItemList<Element>(); 574 575 // look through existing children 576 for (Element child : children) { 577 if (child.getName().equals(name)) { 578 if (!child.isList()) { 579 return child; 580 } else { 581 Element ne = new Element(child).setFormat(format); 582 children.add(ne); 583 ne.index = children.getSizeByName(ne.getListName()) - 1; 584 return ne; 585 } 586 } 587 } 588 589 for (Property p : property.getChildProperties(this.name, type)) { 590 if (p.getName().equals(name)) { 591 Element ne = new Element(name, p).setFormat(format); 592 children.add(ne); 593 ne.index = children.getSizeByName(ne.getListName()) - 1; 594 return ne; 595 } else if (p.getDefinition().isChoice() && name.startsWith(p.getName().replace("[x]", ""))) { 596 String type = name.substring(p.getName().length()-3); 597 if (property.getContext().isPrimitiveType(Utilities.uncapitalize(type))) { 598 type = Utilities.uncapitalize(type); 599 } 600 Element ne = new Element(name, p).setFormat(format); 601 ne.setType(type); 602 children.add(ne); 603 ne.index = children.getSizeByName(ne.getListName()) - 1; 604 return ne; 605 606 } 607 } 608 609 throw new Error("Unrecognised name "+name+" on "+this.name); 610 } 611 612 public Element forceElement(String name) throws FHIRException { 613 if (children == null) 614 children = new NamedItemList<Element>(); 615 616 // look through existing children 617 for (Element child : children) { 618 if (child.getName().equals(name)) { 619 return child; 620 } 621 } 622 623 int index = -1; 624 625 for (Property p : property.getChildProperties(this.name, type)) { 626 if (p.getName().equals(name)) { 627 Element ne = new Element(name, p).setFormat(format); 628 children.add(index+1, ne); 629 for (int i = 0; i < children.size(); i++) { 630 children.get(i).index = i; 631 } 632 return ne; 633 } else { 634 for (int i = 0; i < children.size(); i++) { 635 if (children.get(i).getName().equals(p.getName())) { 636 index = i; 637 } 638 } 639 } 640 } 641 642 throw new Error("Unrecognised name "+name+" on "+this.name); 643 } 644 645 646 private int maxToInt(String max) { 647 if (max.equals("*")) 648 return Integer.MAX_VALUE; 649 else 650 return Integer.parseInt(max); 651 } 652 653 @Override 654 public boolean isPrimitive() { 655 return type != null ? property.isPrimitive(type) : property.isPrimitive(property.getType(name)); 656 } 657 658 @Override 659 public boolean isBooleanPrimitive() { 660 return isPrimitive() && ("boolean".equals(type) || "boolean".equals(property.getType(name))); 661 } 662 663 @Override 664 public boolean isResource() { 665 return property.isResource(); 666 } 667 668 669 @Override 670 public boolean hasPrimitiveValue() { 671 //return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); 672 return super.hasPrimitiveValue(); 673 } 674 675 @Override 676 public boolean canHavePrimitiveValue() { 677 return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); 678 } 679 680 681 @Override 682 public String primitiveValue() { 683 if (isPrimitive() || value != null) 684 return value; 685 else if (hasXhtml()) { 686 return new XhtmlComposer(XhtmlComposer.XML, false).setCanonical(false).compose(xhtml); 687 } else { 688 if (canHavePrimitiveValue() && children != null) { 689 for (Element c : children) { 690 if (c.getName().equals("value")) 691 return c.primitiveValue(); 692 } 693 } 694 return null; 695 } 696 } 697 698 // for the validator 699 public int line() { 700 return line; 701 } 702 703 public int col() { 704 return col; 705 } 706 707 public Element markLocation(int line, int col) { 708 this.line = line; 709 this.col = col; 710 return this; 711 } 712 713 public Element markLocation(SourceLocation loc) { 714 this.line = loc.getLine(); 715 this.col = loc.getColumn(); 716 return this; 717 } 718 719 public Element markLocation(Element src) { 720 this.line = src.line(); 721 this.col = src.col(); 722 return this; 723 } 724 725 public void clearDecorations() { 726 clearUserData(UserDataNames.rendering_xml_decorations); 727 for (Element e : children) { 728 e.clearDecorations(); 729 } 730 } 731 732 public void markValidation(StructureDefinition profile, ElementDefinition definition) { 733 @SuppressWarnings("unchecked") 734 List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData(UserDataNames.rendering_xml_decorations); 735 if (decorations == null) { 736 decorations = new ArrayList<>(); 737 setUserData(UserDataNames.rendering_xml_decorations, decorations); 738 } 739 decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getWebPath(), definition.getPath())); 740 if (definition.getId() != null && tail(definition.getId()).contains(":")) { 741 String[] details = tail(definition.getId()).split(":"); 742 decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1])); 743 } 744 } 745 746 private String tail(String id) { 747 return id.contains(".") ? id.substring(id.lastIndexOf(".")+1) : id; 748 } 749 750 public Element getNamedChild(String name) { 751 return getNamedChild(name, true); 752 } 753 754 public Element getNamedChild(String name, boolean exception) { 755 if (children == null) 756 return null; 757 if (children.size() > 20) { 758 List<Element> l = children.getByName(name); 759 if (l == null || l.size() == 0) { 760 // try the other way (in case of complicated naming rules) 761 } else if (l.size() > 1 && exception) { 762 throw new Error("Attempt to read a single element when there is more than one present ("+name+")"); 763 } else { 764 return l.get(0); 765 } 766 } else { 767 768 } 769 Element result = null; 770 771 for (Element child : children) { 772 if (child.getName() != null && name != null && child.getProperty() != null && child.getProperty().getDefinition() != null && child.fhirType() != null) { 773 if (child.getName().equals(name) || (child.getName().length() > child.fhirType().length() && child.getName().substring(0, child.getName().length() - child.fhirType().length()).equals(name) && child.getProperty().getDefinition().isChoice())) { 774 if (result == null) 775 result = child; 776 else 777 throw new Error("Attempt to read a single element when there is more than one present ("+name+")"); 778 } 779 } 780 } 781 return result; 782 } 783 784 public void getNamedChildren(String name, List<Element> list) { 785 if (children != null) 786 if (children.size() > 20) { 787 List<Element> l = children.getByName(name); 788 if (l != null) { 789 list.addAll(l); 790 } 791 } else { 792 for (Element child : children) 793 if (child.getName().equals(name)) 794 list.add(child); 795 } 796 } 797 798 public String getNamedChildValue(String name) { 799 return getNamedChildValue(name, true); 800 } 801 802 public String getNamedChildValue(String name, boolean exception) { 803 Element child = getNamedChild(name, exception); 804 return child == null ? null : child.value; 805 } 806 807 public void getNamedChildrenWithWildcard(String string, List<Element> values) { 808 Validate.isTrue(string.endsWith("[x]")); 809 810 String start = string.substring(0, string.length() - 3); 811 if (children != null) { 812 for (Element child : children) { 813 if (child.getName().startsWith(start)) { 814 values.add(child); 815 } 816 } 817 } 818 } 819 820 821 public XhtmlNode getXhtml() { 822 return xhtml; 823 } 824 825 public Element setXhtml(XhtmlNode xhtml) { 826 this.xhtml = xhtml; 827 value = new XhtmlComposer(true, false).compose(xhtml); 828 return this; 829 } 830 831 @Override 832 public boolean isEmpty() { 833 // GG: this used to also test !"".equals(value). 834 // the condition where "" is empty and there are no children is an error, and so this really only manifested as an issue in corner cases technical testing of the validator / FHIRPath. 835 // it should not cause any problems in real life. 836 if (value != null) { 837 return false; 838 } 839 for (Element next : getChildren()) { 840 if (!next.isEmpty()) { 841 return false; 842 } 843 } 844 return true; 845 } 846 847 public Property getElementProperty() { 848 return elementProperty; 849 } 850 851 public boolean hasElementProperty() { 852 return elementProperty != null; 853 } 854 855 public boolean hasChild(String name) { 856 return getNamedChild(name, true) != null; 857 } 858 859 public boolean hasChild(String name, boolean exception) { 860 return getNamedChild(name, exception) != null; 861 } 862 863 public boolean canHaveChild(String name) { 864 for (Property p : property.getChildProperties(this.name, type)) { 865 if (name.equals(p.getName())) { 866 return true; 867 } 868 } 869 return false; 870 } 871 872 873 874 public boolean hasChildren(String name) { 875 if (children != null) 876 for (Element child : children) 877 if (child.getName().equals(name)) 878 return true; 879 return false; 880 } 881 882 @Override 883 public String toString() { 884 if (name.equals(fhirType()) && isResource()) { 885 return fhirType()+"/"+getIdBase() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 886 887 } else if (isResource()) { 888 return name+"="+fhirType()+"/"+getIdBase()+ "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 889 } else { 890 return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 891 } 892 } 893 894 @Override 895 public String getIdBase() { 896 return getChildValue("id"); 897 } 898 899 @Override 900 public void setIdBase(String value) { 901 setChildValue("id", value); 902 } 903 904 905 @Override 906 public boolean equalsDeep(Base other) { 907 if (!super.equalsDeep(other)) 908 return false; 909 if (isPrimitive() && primitiveValue() != null && other.isPrimitive()) 910 return primitiveValue().equals(other.primitiveValue()); 911 if (isPrimitive() || other.isPrimitive()) 912 return false; 913 Set<String> processed = new HashSet<String>(); 914 for (org.hl7.fhir.r5.model.Property p : children()) { 915 String name = p.getName(); 916 processed.add(name); 917 org.hl7.fhir.r5.model.Property o = other.getChildByName(name); 918 if (!equalsDeep(p, o)) 919 return false; 920 } 921 for (org.hl7.fhir.r5.model.Property p : children()) { 922 String name = p.getName(); 923 if (!processed.contains(name)) { 924 org.hl7.fhir.r5.model.Property o = other.getChildByName(name); 925 if (!equalsDeep(p, o)) 926 return false; 927 } 928 } 929 return true; 930 } 931 932 private boolean equalsDeep(org.hl7.fhir.r5.model.Property p, org.hl7.fhir.r5.model.Property o) { 933 if (o == null || p == null) 934 return false; 935 if (p.getValues().size() != o.getValues().size()) 936 return false; 937 for (int i = 0; i < p.getValues().size(); i++) 938 if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true)) 939 return false; 940 return true; 941 } 942 943 @Override 944 public boolean equalsShallow(Base other) { 945 if (!super.equalsShallow(other)) 946 return false; 947 if (isPrimitive() && other.isPrimitive()) 948 return primitiveValue().equals(other.primitiveValue()); 949 if (isPrimitive() || other.isPrimitive()) 950 return false; 951 return true; //? 952 } 953 954 public DataType asType() throws FHIRException { 955 return new ObjectConverter(property.getContext()).convertToType(this); 956 } 957 958 @Override 959 public boolean isMetadataBased() { 960 return true; 961 } 962 963 public boolean isList() { 964 if (elementProperty != null) 965 return elementProperty.isList(); 966 else 967 return property.isList(); 968 } 969 970 public boolean isBaseList() { 971 if (elementProperty != null) 972 return elementProperty.isBaseList(); 973 else 974 return property.isBaseList(); 975 } 976 977 @Override 978 public String[] getTypesForProperty(int hash, String name) throws FHIRException { 979 Property p = property.getChildSimpleName(this.name, name); 980 if (p != null) { 981 Set<String> types = new HashSet<String>(); 982 for (TypeRefComponent tr : p.getDefinition().getType()) { 983 types.add(tr.getCode()); 984 } 985 return types.toArray(new String[]{}); 986 } 987 return super.getTypesForProperty(hash, name); 988 989 } 990 991 public void sort() { 992 if (children != null) { 993 List<Element> remove = new ArrayList<Element>(); 994 for (Element child : children) { 995 child.sort(); 996 if (child.isEmpty()) 997 remove.add(child); 998 } 999 children.removeAll(remove); 1000 children.sort(new ElementSortComparator(this, this.property)); 1001 } 1002 } 1003 1004 public class ElementSortComparator implements Comparator<Element> { 1005 private List<ElementDefinition> children; 1006 public ElementSortComparator(Element e, Property property) { 1007 String tn = e.getType(); 1008 StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn, null)); 1009 if (sd != null && !sd.getAbstract()) 1010 children = sd.getSnapshot().getElement(); 1011 else 1012 children = property.getStructure().getSnapshot().getElement(); 1013 } 1014 1015 @Override 1016 public int compare(Element e0, Element e1) { 1017 int i0 = find(e0); 1018 int i1 = find(e1); 1019 return Integer.compare(i0, i1); 1020 } 1021 private int find(Element e0) { 1022 int i = e0.elementProperty != null ? children.indexOf(e0.elementProperty.getDefinition()) : children.indexOf(e0.property.getDefinition()); 1023 return i; 1024 } 1025 1026 } 1027 1028 public class ICodingImpl implements ICoding { 1029 private String system; 1030 private String version; 1031 private String code; 1032 private String display; 1033 private boolean doesSystem; 1034 private boolean doesVersion; 1035 private boolean doesCode; 1036 private boolean doesDisplay; 1037 public ICodingImpl(boolean doesCode, boolean doesSystem, boolean doesVersion, boolean doesDisplay) { 1038 super(); 1039 this.doesCode = doesCode; 1040 this.doesSystem = doesSystem; 1041 this.doesVersion = doesVersion; 1042 this.doesDisplay = doesDisplay; 1043 } 1044 public String getSystem() { 1045 return system; 1046 } 1047 public String getVersion() { 1048 return version; 1049 } 1050 public String getCode() { 1051 return code; 1052 } 1053 public String getDisplay() { 1054 return display; 1055 } 1056 public boolean hasSystem() { 1057 return !Utilities.noString(system); 1058 } 1059 public boolean hasVersion() { 1060 return !Utilities.noString(version); 1061 } 1062 public boolean hasCode() { 1063 return !Utilities.noString(code); 1064 } 1065 public boolean hasDisplay() { 1066 return !Utilities.noString(display); 1067 } 1068 public boolean supportsSystem() { 1069 return doesSystem; 1070 } 1071 public boolean supportsVersion() { 1072 return doesVersion; 1073 } 1074 public boolean supportsCode() { 1075 return doesCode; 1076 } 1077 public boolean supportsDisplay() { 1078 return doesDisplay; 1079 } 1080 } 1081 1082 public ICoding getAsICoding() throws FHIRException { 1083 if ("code".equals(fhirType())) { 1084 if (property.getDefinition().getBinding().getStrength() != BindingStrength.REQUIRED) 1085 return null; 1086 ICodingImpl c = new ICodingImpl(true, true, false, false); 1087 c.code = primitiveValue(); 1088 ValueSetExpansionOutcome vse = property.getContext().expandVS(property.getStructure(), property.getDefinition().getBinding(), true, false); 1089 if (vse.getValueset() == null) 1090 return null; 1091 for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { 1092 if (cc.getCode().equals(c.code)) { 1093 c.system = cc.getSystem(); 1094 if (cc.hasVersion()) { 1095 c.doesVersion = true; 1096 c.version = cc.getVersion(); 1097 } 1098 if (cc.hasDisplay()) { 1099 c.doesDisplay = true; 1100 c.display = cc.getDisplay(); 1101 } 1102 } 1103 } 1104 if (c.system == null) 1105 return null; 1106 return c; 1107 } else if ("Coding".equals(fhirType())) { 1108 ICodingImpl c = new ICodingImpl(true, true, true, true); 1109 c.system = getNamedChildValue("system", false); 1110 c.code = getNamedChildValue("code", false); 1111 c.display = getNamedChildValue("display", false); 1112 c.version = getNamedChildValue("version", false); 1113 return c; 1114 } else if ("Quantity".equals(fhirType())) { 1115 ICodingImpl c = new ICodingImpl(true, true, false, false); 1116 c.system = getNamedChildValue("system", false); 1117 c.code = getNamedChildValue("code", false); 1118 return c; 1119 } else 1120 return null; 1121 } 1122 1123 public String getExplicitType() { 1124 return explicitType; 1125 } 1126 1127 public void setExplicitType(String explicitType) { 1128 this.explicitType = explicitType; 1129 } 1130 1131 public boolean hasDescendant(Element element) { 1132 if (children != null) { 1133 for (Element child : children) { 1134 if (element == child || child.hasDescendant(element)) { 1135 return true; 1136 } 1137 } 1138 } 1139 return false; 1140 } 1141 1142 public Element getExtension(String url) { 1143 if (children != null) { 1144 for (Element child : children) { 1145 if (extensionList.contains(child.getName())) { 1146 String u = child.getChildValue("url"); 1147 if (url.equals(u)) { 1148 return child; 1149 } 1150 } 1151 } 1152 } 1153 return null; 1154 } 1155 1156 public String getExtensionString(String url) { 1157 if (children != null) { 1158 for (Element child : children) { 1159 if (extensionList.contains(child.getName())) { 1160 String u = child.getChildValue("url"); 1161 if (url.equals(u)) { 1162 return child.getNamedChildValue("value"); 1163 } 1164 } 1165 } 1166 } 1167 return null; 1168 } 1169 1170 public List<Element> getExtensions(String url) { 1171 List<Element> list = new ArrayList<>(); 1172 if (children != null) { 1173 for (Element child : children) { 1174 if (extensionList.contains(child.getName())) { 1175 String u = child.getChildValue("url"); 1176 if (url.equals(u)) { 1177 list.add(child); 1178 } 1179 } 1180 } 1181 } 1182 return list; 1183 } 1184 1185 public Base getExtensionValue(String... url) { 1186 if (children != null) { 1187 for (Element child : children) { 1188 if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { 1189 String u = child.getChildValue("url"); 1190 if (Utilities.existsInList(u, url)) { 1191 return child.getNamedChild("value", false); 1192 } 1193 } 1194 } 1195 } 1196 return null; 1197 } 1198 1199 public boolean hasExtension(String... url) { 1200 if (children != null) { 1201 for (Element child : children) { 1202 if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { 1203 String u = child.getChildValue("url"); 1204 if (Utilities.existsInList(u, url)) { 1205 return true; 1206 } 1207 } 1208 } 1209 } 1210 return false; 1211 } 1212 1213 /** 1214 * this is set by the instance validator. There's no reason to maintain this when working with an element tree, and so it should be ignored outside the validator 1215 */ 1216 public Element getParentForValidator() { 1217 if (!hasParentForValidator) { 1218 throw new Error("Parent not set"); 1219 } 1220 return parentForValidator; 1221 } 1222 1223 public void setParentForValidator(Element parentForValidator) { 1224 this.parentForValidator = parentForValidator; 1225 this.hasParentForValidator = true; 1226 } 1227 1228 public boolean hasParentForValidator() { 1229 return hasParentForValidator; 1230 } 1231 1232 public void clear() { 1233 comments = null; 1234 children.clear(); 1235 property = null; 1236 elementProperty = null; 1237 xhtml = null; 1238 path = null; 1239 } 1240 1241 public String getPath() { 1242 return path; 1243 } 1244 1245 public void setPath(String path) { 1246 this.path = path; 1247 } 1248 1249 public void addMessage(ValidationMessage vm) { 1250 if (messages == null) { 1251 messages = new ArrayList<>(); 1252 } 1253 messages.add(vm); 1254 } 1255 1256 public boolean hasMessages() { 1257 return messages != null && !messages.isEmpty(); 1258 } 1259 1260 public List<ValidationMessage> getMessages() { 1261 return messages; 1262 } 1263 1264 public void removeChild(String name) { 1265 if (children.removeIf(n -> name.equals(n.getName()))) { 1266 children.clearMap(); 1267 } 1268 } 1269 1270 public void removeChild(Element child) { 1271 if (children.removeIf(n -> n == child)) { 1272 children.clearMap(); 1273 } 1274 } 1275 1276 public boolean isProhibited() { 1277 return prohibited; 1278 } 1279 1280 public void setProhibited(boolean prohibited) { 1281 this.prohibited = prohibited; 1282 } 1283 1284 public boolean isRequired() { 1285 return required; 1286 } 1287 1288 public void setRequired(boolean required) { 1289 this.required = required; 1290 } 1291 1292 public int getDescendentCount() { 1293 return descendentCount; 1294 } 1295 1296 public void setDescendentCount(int descendentCount) { 1297 this.descendentCount = descendentCount; 1298 } 1299 1300 public int countDescendents() { 1301 if (descendentCount > 0) { 1302 return descendentCount; 1303 } else if (children != null) { 1304 descendentCount = children.size(); 1305 for (Element e : children) { 1306 descendentCount = descendentCount + e.countDescendents(); 1307 } 1308 } else { 1309 descendentCount = 0; 1310 } 1311 return descendentCount; 1312 } 1313 1314 public int getInstanceId() { 1315 return instanceId; 1316 } 1317 1318 public void setInstanceId(int instanceId) { 1319 this.instanceId = instanceId; 1320 } 1321 1322 1323 @Override 1324 public boolean hasValidationInfo() { 1325 return hasSource() ? source.hasValidationInfo() : super.hasValidationInfo(); 1326 } 1327 1328 @Override 1329 public List<ValidationInfo> getValidationInfo() { 1330 return hasSource() ? source.getValidationInfo() : super.getValidationInfo(); 1331 } 1332 1333 @Override 1334 public ValidationInfo addDefinition(StructureDefinition source, ElementDefinition defn, ValidationMode mode) { 1335 if (this.source != null) { 1336 return this.source.addDefinition(source, defn, mode); 1337 } else { 1338 return super.addDefinition(source, defn, mode); 1339 } 1340 } 1341 1342 public boolean hasSource() { 1343 return source != null; 1344 } 1345 1346 1347 public Base getSource() { 1348 return source; 1349 } 1350 1351 public void setSource(Base source) { 1352 this.source = source; 1353 } 1354 1355 @SuppressWarnings("checkstyle:systemout") 1356 public void printToOutput() { 1357 printToOutput(System.out, ""); 1358 } 1359 1360 public void printToOutput(PrintStream stream) { 1361 printToOutput(stream, ""); 1362 1363 } 1364 1365 private void printToOutput(PrintStream out, String indent) { 1366 String s = indent+name +(index == -1 ? "" : "["+index+"]") +(special != null ? "$"+special.toHuman(): "")+ (type!= null || explicitType != null ? " : "+type+(explicitType != null ? "/'"+explicitType+"'" : "") : ""); 1367 if (isNull) { 1368 s = s + " = (null)"; 1369 } else if (value != null) { 1370 s = s + " = '"+value+"'"; 1371 } else if (xhtml != null) { 1372 s = s + " = (xhtml)"; 1373 } 1374 if (property != null) { 1375 s = s +" {"+property.summary(); 1376 if (elementProperty != null) { 1377 s = s +" -> "+elementProperty.summary(); 1378 } 1379 s = s + "}"; 1380 } 1381 if (line > 0) { 1382 s = s + " (l"+line+":c"+col+")"; 1383 } 1384 out.println(s); 1385 if (children != null) { 1386 for (Element child : children) { 1387 child.printToOutput(out, indent+" "); 1388 } 1389 } 1390 1391 } 1392 1393 private String msgCounts() { 1394 int e = 0; 1395 int w = 0; 1396 int h = 0; 1397 for (ValidationMessage msg : messages) { 1398 switch (msg.getLevel()) { 1399 case ERROR: 1400 e++; 1401 break; 1402 case FATAL: 1403 e++; 1404 break; 1405 case INFORMATION: 1406 h++; 1407 break; 1408 case NULL: 1409 break; 1410 case WARNING: 1411 w++; 1412 break; 1413 default: 1414 break; 1415 } 1416 } 1417 return "e:"+e+",w:"+w+",h:"+h; 1418 } 1419 1420 public void populatePaths(String path) { 1421 if (path == null) { 1422 path = fhirType(); 1423 } 1424 setPath(path); 1425 if (children != null) { 1426 for (Element n : children) { 1427 n.populatePaths(path+"."+n.getName()); 1428 } 1429 } 1430 1431 } 1432 1433 public String fhirTypeRoot() { 1434 if (fhirType().contains("/")) { 1435 return fhirType().substring(fhirType().lastIndexOf("/")+1); 1436 } else { 1437 return fhirType(); 1438 } 1439 } 1440 1441 public void setElement(String string, Element map) { 1442 throw new Error("Not done yet"); 1443 } 1444 1445 public Element addElement(String name) { 1446 if (children == null) 1447 children = new NamedItemList<Element>(); 1448 int insertionPoint = 0; 1449 1450 for (Property p : property.getChildProperties(this.name, type)) { 1451 while (insertionPoint < children.size() && nameMatches(children.get(insertionPoint).getName(), p.getName())) { 1452 insertionPoint++; 1453 } 1454 if (p.getName().equals(name)) { 1455 if (!p.isList() && hasChild(name, false)) { 1456 throw new Error(name+" on "+this.name+" is not a list, so can't add an element"); 1457 } 1458 Element ne = new Element(name, p).setFormat(format); 1459 children.add(insertionPoint, ne); 1460 return ne; 1461 } 1462 // polymorphic support 1463 if (p.getName().endsWith("[x]")) { 1464 String base = p.getName().substring(0, p.getName().length()-3); 1465 1466 if (name.startsWith(base)) { 1467 String type = name.substring(base.length()); 1468 if (p.getContextUtils().isPrimitiveType(Utilities.uncapitalize(type))) { 1469 type = Utilities.uncapitalize(type); 1470 } 1471 if (p.canBeType(type)) { 1472 Element ne = new Element(name, p).setFormat(format); 1473 ne.setType(type); 1474 children.add(insertionPoint, ne); 1475 return ne; 1476 } 1477 } 1478 } 1479 } 1480 1481 throw new Error("Unrecognised property '"+name+"' on "+this.name); 1482 } 1483 1484 private boolean nameMatches(String elementName, String propertyName) { 1485 if (propertyName.endsWith("[x]")) { 1486 String base = propertyName.replace("[x]", ""); 1487 return elementName.startsWith(base); 1488 } else { 1489 return elementName.equals(propertyName); 1490 } 1491 } 1492 1493 @Override 1494 public Base copy() { 1495 Element element = new Element(this); 1496 this.copyValues(element); 1497 if (this.isElided()) 1498 element.setElided(true); 1499 return element; 1500 } 1501 1502 @Override 1503 public void copyValues(Base dst) { 1504 super.copyValues(dst); 1505 1506 Element dest = (Element) dst; 1507 if (comments != null) { 1508 dest.comments = new ArrayList<>(); 1509 dest.comments.addAll(comments); 1510 } else { 1511 dest.comments = null; 1512 } 1513 dest.value = value; 1514 if (children != null) { 1515 dest.children = new NamedItemList<>(); 1516 for (Element child : children) { 1517 dest.children.add((Element) child.copy()); 1518 } 1519 } else { 1520 dest.children = null; 1521 } 1522 dest.line = line; 1523 dest.col = col; 1524 dest.xhtml = xhtml; 1525 dest.explicitType = explicitType; 1526 dest.hasParentForValidator = false; 1527 dest.path = path; 1528 dest.messages = null; 1529 dest.prohibited = prohibited; 1530 dest.required = required; 1531 dest.descendentCount = descendentCount; 1532 dest.instanceId = instanceId; 1533 dest.isNull = isNull; 1534 dest.source = source; 1535 dest.format = format; 1536 } 1537 1538 public Base setProperty(String name, Base value) throws FHIRException { 1539 setChildValue(name, value.primitiveValue()); 1540 return this; 1541 } 1542 1543 public boolean isIgnorePropertyOrder() { 1544 return ignorePropertyOrder; 1545 } 1546 1547 public void setIgnorePropertyOrder(boolean ignorePropertyOrder) { 1548 this.ignorePropertyOrder = ignorePropertyOrder; 1549 if (children != null) { 1550 for (Element e : children) { 1551 e.setIgnorePropertyOrder(ignorePropertyOrder); 1552 } 1553 } 1554 } 1555 1556 1557 private String webPath; 1558 public boolean hasWebPath() { 1559 return webPath != null; 1560 } 1561 public String getWebPath() { 1562 return webPath; 1563 } 1564 public void setWebPath(String webPath) { 1565 this.webPath = webPath; 1566 } 1567 1568 public String getTranslation(String lang) { 1569 for (Element e : getChildren()) { 1570 if (e.fhirType().equals("Extension")) { 1571 String url = e.getNamedChildValue("url", false); 1572 if (ToolingExtensions.EXT_TRANSLATION.equals(url)) { 1573 String l = null; 1574 String v = null; 1575 for (Element g : e.getChildren()) { 1576 if (g.fhirType().equals("Extension")) { 1577 String u = g.getNamedChildValue("url", false); 1578 if ("lang".equals(u)) { 1579 l = g.getNamedChildValue("value", false); 1580 } else if ("value".equals(u)) { 1581 v = g.getNamedChildValue("value", false); 1582 } 1583 } 1584 } 1585 if (LanguageUtils.langsMatch(lang, l)) { 1586 return v; 1587 } 1588 } 1589 } 1590 } 1591 return null; 1592 } 1593 1594 public String getBasePath() { 1595 if (property.getStructure().hasExtension(ToolingExtensions.EXT_RESOURCE_IMPLEMENTS)) { 1596 StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ExtensionsUtils.getExtensionString(property.getStructure(), ToolingExtensions.EXT_RESOURCE_IMPLEMENTS)); 1597 if (sd != null) { 1598 ElementDefinition ed = sd.getSnapshot().getElementByPath(property.getDefinition().getPath().replace(property.getStructure().getType(), sd.getType())); 1599 if (ed != null) { 1600 return ed.getBase().getPath(); 1601 } 1602 } 1603 } 1604 return property.getDefinition().getBase().getPath(); 1605 } 1606 1607 public void setTranslation(String lang, String translation) { 1608 for (Element e : getChildren()) { 1609 if (e.fhirType().equals("Extension")) { 1610 String url = e.getNamedChildValue("url", false); 1611 if (ToolingExtensions.EXT_TRANSLATION.equals(url)) { 1612 String l = null; 1613 Element v = null; 1614 for (Element g : e.getChildren()) { 1615 if (g.fhirType().equals("Extension")) { 1616 String u = g.getNamedChildValue("url", false); 1617 if ("lang".equals(u)) { 1618 l = g.getNamedChildValue("value", false); 1619 } else if ("value".equals(u)) { 1620 v = g.getNamedChild("value", false); 1621 } 1622 } 1623 } 1624 if (LanguageUtils.langsMatch(lang, l)) { 1625 if (v == null) { 1626 Element ext = e.addElement("extension"); 1627 ext.addElement("url").setValue("value"); 1628 ext.addElement("valueString").setValue(translation); 1629 } else { 1630 v.setValue(translation); 1631 } 1632 } 1633 } 1634 } 1635 } 1636 Element t = addElement("extension"); 1637 t.addElement("url").setValue(ToolingExtensions.EXT_TRANSLATION); 1638 1639 Element ext = t.addElement("extension"); 1640 ext.addElement("url").setValue("lang"); 1641 ext.addElement("valueCode").setValue(lang); 1642 1643 ext = t.addElement("extension"); 1644 ext.addElement("url").setValue("content"); 1645 ext.addElement("valueString").setValue(translation); 1646 } 1647 1648 @Override 1649 public String getListName() { 1650 if (getProperty().getName().endsWith("[x]")) { 1651 String n = getProperty().getName(); 1652 return n.substring(0, n.length()-3); 1653 } else { 1654 return getName(); 1655 } 1656 } 1657 1658 public FhirFormat getFormat() { 1659 return format; 1660 } 1661 1662 public Element setFormat(FhirFormat format) { 1663 this.format = format; 1664 return this; 1665 } 1666 1667 public Object getNativeObject() { 1668 return nativeObject; 1669 } 1670 1671 public Element setNativeObject(Object nativeObject) { 1672 this.nativeObject = nativeObject; 1673 return this; 1674 } 1675 1676 public void removeExtension(String url) { 1677 if (children != null) { 1678 List<Element> rem = new ArrayList<>(); 1679 for (Element e : children) { 1680 if ("extension".equals(e.getName()) && url.equals(e.getChildValue("url"))) { 1681 rem.add(e); 1682 } 1683 } 1684 children.removeAll(rem); 1685 } 1686 } 1687 1688 public void addSliceDefinition(StructureDefinition profile, ElementDefinition definition, ElementDefinition slice) { 1689 if (sliceDefinitions == null) { 1690 sliceDefinitions = new ArrayList<>(); 1691 } 1692 sliceDefinitions.add(new SliceDefinition(profile, definition, slice)); 1693 } 1694 1695 public boolean hasSlice(StructureDefinition sd, String sliceName) { 1696 if (sliceDefinitions != null) { 1697 for (SliceDefinition def : sliceDefinitions) { 1698 if (def.profile == sd && sliceName.equals(def.definition.getSliceName())) { 1699 return true; 1700 } 1701 } 1702 } 1703 return false; 1704 } 1705 1706 public FhirPublication getFHIRPublicationVersion() { 1707 return FhirPublication.fromCode(property.getStructure().getVersion()); 1708 } 1709 1710 public void setElided(boolean elided) { 1711 this.elided = elided; 1712 } 1713 1714 public boolean isElided() { 1715 return this.elided; 1716 } 1717 1718 public void stripLocations() { 1719 line = -1; 1720 col = -1; 1721 if (children != null) { 1722 for (Element child : children) { 1723 child.stripLocations(); 1724 } 1725 } 1726 } 1727 1728 public void sortChildren(Comparator<Element> sorter) { 1729 children.sort(sorter); 1730 } 1731 1732 public String getStatedResourceId() { 1733 for (Property p : getProperty().getChildProperties(null)) { 1734 if (ToolingExtensions.readBoolExtension(p.getDefinition(), ToolingExtensions.EXT_USE_AS_RESOURCE_ID)) { 1735 return getNamedChildValue(p.getName()); 1736 } 1737 } 1738 return null; 1739 } 1740 1741 public boolean hasXhtml() { 1742 return xhtml != null; 1743 } 1744}