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