001package org.hl7.fhir.r5.elementmodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033import java.util.ArrayList; 034import java.util.Collections; 035import java.util.Comparator; 036import java.util.HashMap; 037import java.util.HashSet; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041 042import org.apache.commons.lang3.Validate; 043import org.hl7.fhir.exceptions.FHIRException; 044import org.hl7.fhir.r5.conformance.ProfileUtilities; 045import org.hl7.fhir.r5.model.Base; 046import org.hl7.fhir.r5.model.DataType; 047import org.hl7.fhir.r5.model.ElementDefinition; 048import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 049import org.hl7.fhir.r5.model.Enumerations.BindingStrength; 050import org.hl7.fhir.r5.model.ICoding; 051import org.hl7.fhir.r5.model.StringType; 052import org.hl7.fhir.r5.model.StructureDefinition; 053import org.hl7.fhir.r5.model.TypeConvertor; 054import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 055import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; 056import org.hl7.fhir.utilities.ElementDecoration; 057import org.hl7.fhir.utilities.ElementDecoration.DecorationType; 058import org.hl7.fhir.utilities.validation.ValidationMessage; 059import org.hl7.fhir.utilities.Utilities; 060import org.hl7.fhir.utilities.xhtml.XhtmlNode; 061 062/** 063 * This class represents the underlying reference model of FHIR 064 * 065 * A resource is nothing but a set of elements, where every element has a 066 * name, maybe a stated type, maybe an id, and either a value or child elements 067 * (one or the other, but not both or neither) 068 * 069 * @author Grahame Grieve 070 * 071 */ 072public class Element extends Base { 073 074 075 public enum SpecialElement { 076 CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER; 077 078 public static SpecialElement fromProperty(Property property) { 079 if (property.getStructure().getType().equals("Parameters")) 080 return PARAMETER; 081 if (property.getStructure().getType().equals("Bundle") && property.getName().equals("resource")) 082 return BUNDLE_ENTRY; 083 if (property.getStructure().getType().equals("Bundle") && property.getName().equals("outcome")) 084 return BUNDLE_OUTCOME; 085 if (property.getName().equals("contained")) 086 return CONTAINED; 087 throw new FHIRException("Unknown resource containing a native resource: "+property.getDefinition().getId()); 088 } 089 090 public String toHuman() { 091 switch (this) { 092 case BUNDLE_ENTRY: return "entry"; 093 case BUNDLE_OUTCOME: return "outcome"; 094 case CONTAINED: return "contained"; 095 case PARAMETER: return "parameter"; 096 default: return "??"; 097 } 098 } 099 } 100 101 private List<String> comments;// not relevant for production, but useful in documentation 102 private String name; 103 private String type; 104 private String value; 105 private int index = -1; 106 private List<Element> children; 107 private Property property; 108 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 109 private int line; 110 private int col; 111 private SpecialElement special; 112 private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation 113 private String explicitType; // for xsi:type attribute 114 private Element parentForValidator; 115 private boolean hasParentForValidator; 116 private String path; 117 private List<ValidationMessage> messages; 118 private boolean prohibited; 119 private boolean required; 120 121 public Element(String name) { 122 super(); 123 this.name = name; 124 } 125 126 public Element(Element other) { 127 super(); 128 name = other.name; 129 type = other.type; 130 property = other.property; 131 elementProperty = other.elementProperty; 132 special = other.special; 133 } 134 135 public Element(String name, Property property) { 136 super(); 137 this.name = name; 138 this.property = property; 139 } 140 141 public Element(String name, Property property, String type, String value) { 142 super(); 143 this.name = name; 144 this.property = property; 145 this.type = type; 146 this.value = value; 147 } 148 149 public void updateProperty(Property property, SpecialElement special, Property elementProperty) { 150 this.property = property; 151 this.elementProperty = elementProperty; 152 this.special = special; 153 } 154 155 public SpecialElement getSpecial() { 156 return special; 157 } 158 159 public String getName() { 160 return name; 161 } 162 163 public String getType() { 164 if (type == null) 165 return property.getType(name); 166 else 167 return type; 168 } 169 170 public String getValue() { 171 return value; 172 } 173 174 public boolean hasChildren() { 175 return !(children == null || children.isEmpty()); 176 } 177 178 public List<Element> getChildren() { 179 if (children == null) 180 children = new ArrayList<Element>(); 181 return children; 182 } 183 184 public boolean hasComments() { 185 return !(comments == null || comments.isEmpty()); 186 } 187 188 public List<String> getComments() { 189 if (comments == null) 190 comments = new ArrayList<String>(); 191 return comments; 192 } 193 194 public Property getProperty() { 195 return property; 196 } 197 198 public void setValue(String value) { 199 this.value = value; 200 } 201 202 public void setType(String type) { 203 this.type = type; 204 205 } 206 207 public boolean hasValue() { 208 return value != null; 209 } 210 211 public List<Element> getChildrenByName(String name) { 212 List<Element> res = new ArrayList<Element>(); 213 if (hasChildren()) { 214 for (Element child : children) 215 if (name.equals(child.getName())) 216 res.add(child); 217 } 218 return res; 219 } 220 221 public void numberChildren() { 222 if (children == null) 223 return; 224 225 String last = ""; 226 int index = 0; 227 for (Element child : children) { 228 if (child.getProperty().isList()) { 229 if (last.equals(child.getName())) { 230 index++; 231 } else { 232 last = child.getName(); 233 index = 0; 234 } 235 child.index = index; 236 } else { 237 child.index = -1; 238 } 239 child.numberChildren(); 240 } 241 } 242 243 public int getIndex() { 244 return index; 245 } 246 247 public boolean hasIndex() { 248 return index > -1; 249 } 250 251 public void setIndex(int index) { 252 this.index = index; 253 } 254 255 public String getChildValue(String name) { 256 if (children == null) 257 return null; 258 for (Element child : children) { 259 if (name.equals(child.getName())) 260 return child.getValue(); 261 } 262 return null; 263 } 264 265 public void setChildValue(String name, String value) { 266 if (children == null) 267 children = new ArrayList<Element>(); 268 for (Element child : children) { 269 if (name.equals(child.getName())) { 270 if (!child.isPrimitive()) 271 throw new Error("Cannot set a value of a non-primitive type ("+name+" on "+this.getName()+")"); 272 child.setValue(value); 273 } 274 } 275 try { 276 setProperty(name.hashCode(), name, new StringType(value)); 277 } catch (FHIRException e) { 278 throw new Error(e); 279 } 280 } 281 282 public List<Element> getChildren(String name) { 283 List<Element> res = new ArrayList<Element>(); 284 if (children != null) 285 for (Element child : children) { 286 if (name.equals(child.getName())) 287 res.add(child); 288 } 289 return res; 290 } 291 292 public boolean hasType() { 293 if (type == null) 294 return property.hasType(name); 295 else 296 return true; 297 } 298 299 @Override 300 public String fhirType() { 301 return getType(); 302 } 303 304 @Override 305 public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { 306 if (isPrimitive() && (hash == "value".hashCode()) && !Utilities.noString(value)) { 307// String tn = getType(); 308// throw new Error(tn+" not done yet"); 309 Base[] b = new Base[1]; 310 b[0] = new StringType(value); 311 return b; 312 } 313 314 List<Base> result = new ArrayList<Base>(); 315 if (children != null) { 316 for (Element child : children) { 317 if (child.getName().equals(name)) 318 result.add(child); 319 if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]")) 320 result.add(child); 321 } 322 } 323 if (result.isEmpty() && checkValid) { 324// throw new FHIRException("not determined yet"); 325 } 326 return result.toArray(new Base[result.size()]); 327 } 328 329 @Override 330 protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) { 331 if (children != null) { 332 Map<String, org.hl7.fhir.r5.model.Property> map = new HashMap<String, org.hl7.fhir.r5.model.Property>(); 333 for (Element c : children) { 334 org.hl7.fhir.r5.model.Property p = map.get(c.getName()); 335 if (p == null) { 336 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); 337 childProps.add(p); 338 map.put(c.getName(), p); 339 340 } else 341 p.getValues().add(c); 342 } 343 } 344 } 345 346 @Override 347 public Base setProperty(int hash, String name, Base value) throws FHIRException { 348 if ("xhtml".equals(getType()) && (hash == "value".hashCode())) { 349 this.xhtml = TypeConvertor.castToXhtml(value); 350 this.value = TypeConvertor.castToXhtmlString(value); 351 return this; 352 } 353 if (isPrimitive() && (hash == "value".hashCode())) { 354 this.value = TypeConvertor.castToString(value).asStringValue(); 355 return this; 356 } 357 358 if (!value.isPrimitive() && !(value instanceof Element)) { 359 if (isDataType(value)) 360 value = convertToElement(property.getChild(name), value); 361 else 362 throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type"); 363 } 364 365 if (children == null) 366 children = new ArrayList<Element>(); 367 Element childForValue = null; 368 369 // look through existing children 370 for (Element child : children) { 371 if (child.getName().equals(name)) { 372 if (!child.isList()) { 373 childForValue = child; 374 break; 375 } else { 376 Element ne = new Element(child); 377 children.add(ne); 378 numberChildren(); 379 childForValue = ne; 380 break; 381 } 382 } 383 } 384 385 int i = 0; 386 if (childForValue == null) 387 for (Property p : property.getChildProperties(this.name, type)) { 388 int t = -1; 389 for (int c =0; c < children.size(); c++) { 390 Element e = children.get(c); 391 if (p.getName().equals(e.getName())) 392 t = c; 393 } 394 if (t >= i) 395 i = t+1; 396 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 397 Element ne = new Element(name, p); 398 children.add(i, ne); 399 childForValue = ne; 400 break; 401 } 402 } 403 404 if (childForValue == null) 405 throw new Error("Cannot set property "+name+" on "+this.name); 406 else if (value.isPrimitive()) { 407 if (childForValue.property.getName().endsWith("[x]")) 408 childForValue.name = name+Utilities.capitalize(value.fhirType()); 409 childForValue.setValue(value.primitiveValue()); 410 } else { 411 Element ve = (Element) value; 412 childForValue.type = ve.getType(); 413 if (childForValue.property.getName().endsWith("[x]")) 414 childForValue.name = name+Utilities.capitalize(childForValue.type); 415 else if (value.isResource()) { 416 if (childForValue.elementProperty == null) 417 childForValue.elementProperty = childForValue.property; 418 childForValue.property = ve.property; 419 childForValue.special = SpecialElement.BUNDLE_ENTRY; 420 } 421 if (ve.children != null) { 422 if (childForValue.children == null) 423 childForValue.children = new ArrayList<Element>(); 424 else 425 childForValue.children.clear(); 426 childForValue.children.addAll(ve.children); 427 } 428 } 429 return childForValue; 430 } 431 432 private Base convertToElement(Property prop, Base v) throws FHIRException { 433 return new ObjectConverter(property.getContext()).convert(prop, (DataType) v); 434 } 435 436 private boolean isDataType(Base v) { 437 return v instanceof DataType && property.getContext().getTypeNames().contains(v.fhirType()); 438 } 439 440 @Override 441 public Base makeProperty(int hash, String name) throws FHIRException { 442 if (isPrimitive() && (hash == "value".hashCode())) { 443 return new StringType(value); 444 } 445 446 if (children == null) 447 children = new ArrayList<Element>(); 448 449 // look through existing children 450 for (Element child : children) { 451 if (child.getName().equals(name)) { 452 if (!child.isList()) { 453 return child; 454 } else { 455 Element ne = new Element(child); 456 children.add(ne); 457 numberChildren(); 458 return ne; 459 } 460 } 461 } 462 463 for (Property p : property.getChildProperties(this.name, type)) { 464 if (p.getName().equals(name)) { 465 Element ne = new Element(name, p); 466 children.add(ne); 467 return ne; 468 } 469 } 470 471 throw new Error("Unrecognised name "+name+" on "+this.name); 472 } 473 474 private int maxToInt(String max) { 475 if (max.equals("*")) 476 return Integer.MAX_VALUE; 477 else 478 return Integer.parseInt(max); 479 } 480 481 @Override 482 public boolean isPrimitive() { 483 return type != null ? property.isPrimitive(type) : property.isPrimitive(property.getType(name)); 484 } 485 486 @Override 487 public boolean isBooleanPrimitive() { 488 return isPrimitive() && ("boolean".equals(type) || "boolean".equals(property.getType(name))); 489 } 490 491 @Override 492 public boolean isResource() { 493 return property.isResource(); 494 } 495 496 497 @Override 498 public boolean hasPrimitiveValue() { 499 return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); 500 } 501 502 503 @Override 504 public String primitiveValue() { 505 if (isPrimitive()) 506 return value; 507 else { 508 if (hasPrimitiveValue() && children != null) { 509 for (Element c : children) { 510 if (c.getName().equals("value")) 511 return c.primitiveValue(); 512 } 513 } 514 return null; 515 } 516 } 517 518 // for the validator 519 public int line() { 520 return line; 521 } 522 523 public int col() { 524 return col; 525 } 526 527 public Element markLocation(int line, int col) { 528 this.line = line; 529 this.col = col; 530 return this; 531 } 532 533 public void clearDecorations() { 534 clearUserData("fhir.decorations"); 535 for (Element e : children) 536 e.clearDecorations(); 537 } 538 539 public void markValidation(StructureDefinition profile, ElementDefinition definition) { 540 @SuppressWarnings("unchecked") 541 List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations"); 542 if (decorations == null) { 543 decorations = new ArrayList<>(); 544 setUserData("fhir.decorations", decorations); 545 } 546 decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath())); 547 if (definition.getId() != null && tail(definition.getId()).contains(":")) { 548 String[] details = tail(definition.getId()).split(":"); 549 decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1])); 550 } 551 } 552 553 private String tail(String id) { 554 return id.contains(".") ? id.substring(id.lastIndexOf(".")+1) : id; 555 } 556 557 public Element getNamedChild(String name) { 558 if (children == null) 559 return null; 560 Element result = null; 561 for (Element child : children) { 562 if (child.getName() != null && name != null && child.getProperty() != null && child.getProperty().getDefinition() != null && child.fhirType() != null) { 563 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())) { 564 if (result == null) 565 result = child; 566 else 567 throw new Error("Attempt to read a single element when there is more than one present ("+name+")"); 568 } 569 } 570 } 571 return result; 572 } 573 574 public void getNamedChildren(String name, List<Element> list) { 575 if (children != null) 576 for (Element child : children) 577 if (child.getName().equals(name)) 578 list.add(child); 579 } 580 581 public String getNamedChildValue(String name) { 582 Element child = getNamedChild(name); 583 return child == null ? null : child.value; 584 } 585 586 public void getNamedChildrenWithWildcard(String string, List<Element> values) { 587 Validate.isTrue(string.endsWith("[x]")); 588 589 String start = string.substring(0, string.length() - 3); 590 if (children != null) { 591 for (Element child : children) { 592 if (child.getName().startsWith(start)) { 593 values.add(child); 594 } 595 } 596 } 597 } 598 599 600 public XhtmlNode getXhtml() { 601 return xhtml; 602 } 603 604 public Element setXhtml(XhtmlNode xhtml) { 605 this.xhtml = xhtml; 606 return this; 607 } 608 609 @Override 610 public boolean isEmpty() { 611 // GG: this used to also test !"".equals(value). 612 // 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. 613 // it should not cause any problems in real life. 614 if (value != null) { 615 return false; 616 } 617 for (Element next : getChildren()) { 618 if (!next.isEmpty()) { 619 return false; 620 } 621 } 622 return true; 623 } 624 625 public Property getElementProperty() { 626 return elementProperty; 627 } 628 629 public boolean hasElementProperty() { 630 return elementProperty != null; 631 } 632 633 public boolean hasChild(String name) { 634 return getNamedChild(name) != null; 635 } 636 637 public boolean hasChildren(String name) { 638 if (children != null) 639 for (Element child : children) 640 if (child.getName().equals(name)) 641 return true; 642 return false; 643 } 644 645 @Override 646 public String toString() { 647 if (name.equals(fhirType()) && isResource()) { 648 return fhirType()+"/"+getIdBase() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 649 650 } else if (isResource()) { 651 return name+"="+fhirType()+"/"+getIdBase()+ "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 652 } else { 653 return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]"; 654 } 655 } 656 657 @Override 658 public String getIdBase() { 659 return getChildValue("id"); 660 } 661 662 @Override 663 public void setIdBase(String value) { 664 setChildValue("id", value); 665 } 666 667 668 @Override 669 public boolean equalsDeep(Base other) { 670 if (!super.equalsDeep(other)) 671 return false; 672 if (isPrimitive() && other.isPrimitive()) 673 return primitiveValue().equals(other.primitiveValue()); 674 if (isPrimitive() || other.isPrimitive()) 675 return false; 676 Set<String> processed = new HashSet<String>(); 677 for (org.hl7.fhir.r5.model.Property p : children()) { 678 String name = p.getName(); 679 processed.add(name); 680 org.hl7.fhir.r5.model.Property o = other.getChildByName(name); 681 if (!equalsDeep(p, o)) 682 return false; 683 } 684 for (org.hl7.fhir.r5.model.Property p : children()) { 685 String name = p.getName(); 686 if (!processed.contains(name)) { 687 org.hl7.fhir.r5.model.Property o = other.getChildByName(name); 688 if (!equalsDeep(p, o)) 689 return false; 690 } 691 } 692 return true; 693 } 694 695 private boolean equalsDeep(org.hl7.fhir.r5.model.Property p, org.hl7.fhir.r5.model.Property o) { 696 if (o == null || p == null) 697 return false; 698 if (p.getValues().size() != o.getValues().size()) 699 return false; 700 for (int i = 0; i < p.getValues().size(); i++) 701 if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true)) 702 return false; 703 return true; 704 } 705 706 @Override 707 public boolean equalsShallow(Base other) { 708 if (!super.equalsShallow(other)) 709 return false; 710 if (isPrimitive() && other.isPrimitive()) 711 return primitiveValue().equals(other.primitiveValue()); 712 if (isPrimitive() || other.isPrimitive()) 713 return false; 714 return true; //? 715 } 716 717 public DataType asType() throws FHIRException { 718 return new ObjectConverter(property.getContext()).convertToType(this); 719 } 720 721 @Override 722 public boolean isMetadataBased() { 723 return true; 724 } 725 726 public boolean isList() { 727 if (elementProperty != null) 728 return elementProperty.isList(); 729 else 730 return property.isList(); 731 } 732 733 public boolean isBaseList() { 734 if (elementProperty != null) 735 return elementProperty.isBaseList(); 736 else 737 return property.isBaseList(); 738 } 739 740 @Override 741 public String[] getTypesForProperty(int hash, String name) throws FHIRException { 742 Property p = property.getChildSimpleName(this.name, name); 743 if (p != null) { 744 Set<String> types = new HashSet<String>(); 745 for (TypeRefComponent tr : p.getDefinition().getType()) { 746 types.add(tr.getCode()); 747 } 748 return types.toArray(new String[]{}); 749 } 750 return super.getTypesForProperty(hash, name); 751 752 } 753 754 public void sort() { 755 if (children != null) { 756 List<Element> remove = new ArrayList<Element>(); 757 for (Element child : children) { 758 child.sort(); 759 if (child.isEmpty()) 760 remove.add(child); 761 } 762 children.removeAll(remove); 763 Collections.sort(children, new ElementSortComparator(this, this.property)); 764 } 765 } 766 767 public class ElementSortComparator implements Comparator<Element> { 768 private List<ElementDefinition> children; 769 public ElementSortComparator(Element e, Property property) { 770 String tn = e.getType(); 771 StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn, property.getContext().getOverrideVersionNs())); 772 if (sd != null && !sd.getAbstract()) 773 children = sd.getSnapshot().getElement(); 774 else 775 children = property.getStructure().getSnapshot().getElement(); 776 } 777 778 @Override 779 public int compare(Element e0, Element e1) { 780 int i0 = find(e0); 781 int i1 = find(e1); 782 return Integer.compare(i0, i1); 783 } 784 private int find(Element e0) { 785 int i = e0.elementProperty != null ? children.indexOf(e0.elementProperty.getDefinition()) : children.indexOf(e0.property.getDefinition()); 786 return i; 787 } 788 789 } 790 791 public class ICodingImpl implements ICoding { 792 private String system; 793 private String version; 794 private String code; 795 private String display; 796 private boolean doesSystem; 797 private boolean doesVersion; 798 private boolean doesCode; 799 private boolean doesDisplay; 800 public ICodingImpl(boolean doesCode, boolean doesSystem, boolean doesVersion, boolean doesDisplay) { 801 super(); 802 this.doesCode = doesCode; 803 this.doesSystem = doesSystem; 804 this.doesVersion = doesVersion; 805 this.doesDisplay = doesDisplay; 806 } 807 public String getSystem() { 808 return system; 809 } 810 public String getVersion() { 811 return version; 812 } 813 public String getCode() { 814 return code; 815 } 816 public String getDisplay() { 817 return display; 818 } 819 public boolean hasSystem() { 820 return !Utilities.noString(system); 821 } 822 public boolean hasVersion() { 823 return !Utilities.noString(version); 824 } 825 public boolean hasCode() { 826 return !Utilities.noString(code); 827 } 828 public boolean hasDisplay() { 829 return !Utilities.noString(display); 830 } 831 public boolean supportsSystem() { 832 return doesSystem; 833 } 834 public boolean supportsVersion() { 835 return doesVersion; 836 } 837 public boolean supportsCode() { 838 return doesCode; 839 } 840 public boolean supportsDisplay() { 841 return doesDisplay; 842 } 843 } 844 845 public ICoding getAsICoding() throws FHIRException { 846 if ("code".equals(fhirType())) { 847 if (property.getDefinition().getBinding().getStrength() != BindingStrength.REQUIRED) 848 return null; 849 ICodingImpl c = new ICodingImpl(true, true, false, false); 850 c.code = primitiveValue(); 851 ValueSetExpansionOutcome vse = property.getContext().expandVS(property.getDefinition().getBinding(), true, false); 852 if (vse.getValueset() == null) 853 return null; 854 for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { 855 if (cc.getCode().equals(c.code)) { 856 c.system = cc.getSystem(); 857 if (cc.hasVersion()) { 858 c.doesVersion = true; 859 c.version = cc.getVersion(); 860 } 861 if (cc.hasDisplay()) { 862 c.doesDisplay = true; 863 c.display = cc.getDisplay(); 864 } 865 } 866 } 867 if (c.system == null) 868 return null; 869 return c; 870 } else if ("Coding".equals(fhirType())) { 871 ICodingImpl c = new ICodingImpl(true, true, true, true); 872 c.system = getNamedChildValue("system"); 873 c.code = getNamedChildValue("code"); 874 c.display = getNamedChildValue("display"); 875 c.version = getNamedChildValue("version"); 876 return c; 877 } else if ("Quantity".equals(fhirType())) { 878 ICodingImpl c = new ICodingImpl(true, true, false, false); 879 c.system = getNamedChildValue("system"); 880 c.code = getNamedChildValue("code"); 881 return c; 882 } else 883 return null; 884 } 885 886 public String getExplicitType() { 887 return explicitType; 888 } 889 890 public void setExplicitType(String explicitType) { 891 this.explicitType = explicitType; 892 } 893 894 public boolean hasDescendant(Element element) { 895 if (children != null) { 896 for (Element child : children) { 897 if (element == child || child.hasDescendant(element)) { 898 return true; 899 } 900 } 901 } 902 return false; 903 } 904 905 public Element getExtension(String url) { 906 if (children != null) { 907 for (Element child : children) { 908 if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { 909 String u = child.getChildValue("url"); 910 if (url.equals(u)) { 911 return child; 912 } 913 } 914 } 915 } 916 return null; 917 } 918 919 public Base getExtensionValue(String url) { 920 if (children != null) { 921 for (Element child : children) { 922 if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { 923 String u = child.getChildValue("url"); 924 if (url.equals(u)) { 925 return child.getNamedChild("value"); 926 } 927 } 928 } 929 } 930 return null; 931 } 932 933 public boolean hasExtension(String url) { 934 if (children != null) { 935 for (Element child : children) { 936 if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { 937 String u = child.getChildValue("url"); 938 if (url.equals(u)) { 939 return true; 940 } 941 } 942 } 943 } 944 return false; 945 } 946 947 /** 948 * 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 949 */ 950 public Element getParentForValidator() { 951 if (!hasParentForValidator) { 952 throw new Error("Parent not set"); 953 } 954 return parentForValidator; 955 } 956 957 public void setParentForValidator(Element parentForValidator) { 958 this.parentForValidator = parentForValidator; 959 this.hasParentForValidator = true; 960 } 961 962 public boolean hasParentForValidator() { 963 return hasParentForValidator; 964 } 965 966 public void clear() { 967 comments = null; 968 children.clear();; 969 property = null; 970 elementProperty = null; 971 xhtml = null; 972 path = null; 973 } 974 975 public String getPath() { 976 return path; 977 } 978 979 public void setPath(String path) { 980 this.path = path; 981 } 982 983 public void addMessage(ValidationMessage vm) { 984 if (messages == null) { 985 messages = new ArrayList<>(); 986 } 987 messages.add(vm); 988 } 989 990 public boolean hasMessages() { 991 return messages != null && !messages.isEmpty(); 992 } 993 994 public List<ValidationMessage> getMessages() { 995 return messages; 996 } 997 998 public void removeChild(String name) { 999 children.removeIf(n -> name.equals(n.getName())); 1000 } 1001 1002 public boolean isProhibited() { 1003 return prohibited; 1004 } 1005 1006 public void setProhibited(boolean prohibited) { 1007 this.prohibited = prohibited; 1008 } 1009 1010 public boolean isRequired() { 1011 return required; 1012 } 1013 1014 public void setRequired(boolean required) { 1015 this.required = required; 1016 } 1017 1018 1019}