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