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