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