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