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