001package org.hl7.fhir.r4.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
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.Comparator;
035import java.util.HashMap;
036import java.util.HashSet;
037import java.util.List;
038import java.util.Map;
039import java.util.Set;
040
041import org.apache.commons.lang3.Validate;
042import org.hl7.fhir.exceptions.FHIRException;
043import org.hl7.fhir.r4.conformance.ProfileUtilities;
044import org.hl7.fhir.r4.model.Base;
045import org.hl7.fhir.r4.model.ElementDefinition;
046import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
047import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
048import org.hl7.fhir.r4.model.ICoding;
049import org.hl7.fhir.r4.model.StringType;
050import org.hl7.fhir.r4.model.StructureDefinition;
051import org.hl7.fhir.r4.model.Type;
052import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
053import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
054import org.hl7.fhir.utilities.ElementDecoration;
055import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
056import org.hl7.fhir.utilities.Utilities;
057import org.hl7.fhir.utilities.xhtml.XhtmlNode;
058
059/**
060 * This class represents the underlying reference model of FHIR
061 * 
062 * A resource is nothing but a set of elements, where every element has a name,
063 * maybe a stated type, maybe an id, and either a value or child elements (one
064 * or the other, but not both or neither)
065 * 
066 * @author Grahame Grieve
067 *
068 */
069public class Element extends Base {
070
071  public enum SpecialElement {
072    CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER;
073
074    public static SpecialElement fromProperty(Property property) {
075      if (property.getStructure().getIdElement().getIdPart().equals("Parameters"))
076        return PARAMETER;
077      if (property.getStructure().getIdElement().getIdPart().equals("Bundle") && property.getName().equals("resource"))
078        return BUNDLE_ENTRY;
079      if (property.getStructure().getIdElement().getIdPart().equals("Bundle") && property.getName().equals("outcome"))
080        return BUNDLE_OUTCOME;
081      if (property.getName().equals("contained"))
082        return CONTAINED;
083      throw new Error("Unknown resource containing a native resource: " + property.getDefinition().getId());
084    }
085  }
086
087  private List<String> comments;// not relevant for production, but useful in documentation
088  private String name;
089  private String type;
090  private String value;
091  private int index = -1;
092  private List<Element> children;
093  private Property property;
094  private Property elementProperty; // this is used when special is set to true - it tracks the underlying element
095                                    // property which is used in a few places
096  private int line;
097  private int col;
098  private SpecialElement special;
099  private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation
100  private String explicitType; // for xsi:type attribute
101
102  public Element(String name) {
103    super();
104    this.name = name;
105  }
106
107  public Element(Element other) {
108    super();
109    name = other.name;
110    type = other.type;
111    property = other.property;
112    elementProperty = other.elementProperty;
113    special = other.special;
114  }
115
116  public Element(String name, Property property) {
117    super();
118    this.name = name;
119    this.property = property;
120  }
121
122  public Element(String name, Property property, String type, String value) {
123    super();
124    this.name = name;
125    this.property = property;
126    this.type = type;
127    this.value = value;
128  }
129
130  public void updateProperty(Property property, SpecialElement special, Property elementProperty) {
131    this.property = property;
132    this.elementProperty = elementProperty;
133    this.special = special;
134  }
135
136  public SpecialElement getSpecial() {
137    return special;
138  }
139
140  public String getName() {
141    return name;
142  }
143
144  public String getType() {
145    if (type == null)
146      return property.getType(name);
147    else
148      return type;
149  }
150
151  public String getValue() {
152    return value;
153  }
154
155  public boolean hasChildren() {
156    return !(children == null || children.isEmpty());
157  }
158
159  public List<Element> getChildren() {
160    if (children == null)
161      children = new ArrayList<Element>();
162    return children;
163  }
164
165  public boolean hasComments() {
166    return !(comments == null || comments.isEmpty());
167  }
168
169  public List<String> getComments() {
170    if (comments == null)
171      comments = new ArrayList<String>();
172    return comments;
173  }
174
175  public Property getProperty() {
176    return property;
177  }
178
179  public void setValue(String value) {
180    this.value = value;
181  }
182
183  public void setType(String type) {
184    this.type = type;
185
186  }
187
188  public boolean hasValue() {
189    return value != null;
190  }
191
192  public List<Element> getChildrenByName(String name) {
193    List<Element> res = new ArrayList<Element>();
194    if (hasChildren()) {
195      for (Element child : children)
196        if (name.equals(child.getName()))
197          res.add(child);
198    }
199    return res;
200  }
201
202  public void numberChildren() {
203    if (children == null)
204      return;
205
206    String last = "";
207    int index = 0;
208    for (Element child : children) {
209      if (child.getProperty().isList()) {
210        if (last.equals(child.getName())) {
211          index++;
212        } else {
213          last = child.getName();
214          index = 0;
215        }
216        child.index = index;
217      } else {
218        child.index = -1;
219      }
220      child.numberChildren();
221    }
222  }
223
224  public int getIndex() {
225    return index;
226  }
227
228  public boolean hasIndex() {
229    return index > -1;
230  }
231
232  public void setIndex(int index) {
233    this.index = index;
234  }
235
236  public String getChildValue(String name) {
237    if (children == null)
238      return null;
239    for (Element child : children) {
240      if (name.equals(child.getName()))
241        return child.getValue();
242    }
243    return null;
244  }
245
246  public void setChildValue(String name, String value) {
247    if (children == null)
248      children = new ArrayList<Element>();
249    for (Element child : children) {
250      if (name.equals(child.getName())) {
251        if (!child.isPrimitive())
252          throw new Error("Cannot set a value of a non-primitive type (" + name + " on " + this.getName() + ")");
253        child.setValue(value);
254      }
255    }
256    try {
257      setProperty(name.hashCode(), name, new StringType(value));
258    } catch (FHIRException e) {
259      throw new Error(e);
260    }
261  }
262
263  public List<Element> getChildren(String name) {
264    List<Element> res = new ArrayList<Element>();
265    if (children != null)
266      for (Element child : children) {
267        if (name.equals(child.getName()))
268          res.add(child);
269      }
270    return res;
271  }
272
273  public boolean hasType() {
274    if (type == null)
275      return property.hasType(name);
276    else
277      return true;
278  }
279
280  @Override
281  public String fhirType() {
282    return getType();
283  }
284
285  @Override
286  public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
287    if (isPrimitive() && (hash == "value".hashCode()) && !Utilities.noString(value)) {
288//              String tn = getType();
289//              throw new Error(tn+" not done yet");
290      Base[] b = new Base[1];
291      b[0] = new StringType(value);
292      return b;
293    }
294
295    List<Base> result = new ArrayList<Base>();
296    if (children != null) {
297      for (Element child : children) {
298        if (child.getName().equals(name))
299          result.add(child);
300        if (child.getName().startsWith(name) && child.getProperty().isChoice()
301            && child.getProperty().getName().equals(name + "[x]"))
302          result.add(child);
303      }
304    }
305    if (result.isEmpty() && checkValid) {
306//              throw new FHIRException("not determined yet");
307    }
308    return result.toArray(new Base[result.size()]);
309  }
310
311  @Override
312  protected void listChildren(List<org.hl7.fhir.r4.model.Property> childProps) {
313    if (children != null) {
314      Map<String, org.hl7.fhir.r4.model.Property> map = new HashMap<String, org.hl7.fhir.r4.model.Property>();
315      for (Element c : children) {
316        org.hl7.fhir.r4.model.Property p = map.get(c.getName());
317        if (p == null) {
318          p = new org.hl7.fhir.r4.model.Property(c.getName(), c.fhirType(),
319              c.getProperty().getDefinition().getDefinition(), c.getProperty().getDefinition().getMin(),
320              maxToInt(c.getProperty().getDefinition().getMax()), c);
321          childProps.add(p);
322          map.put(c.getName(), p);
323
324        } else
325          p.getValues().add(c);
326      }
327    }
328  }
329
330  @Override
331  public Base copy() {
332    Element element = new Element(this);
333    this.copyValues(element);
334    return element;
335  }
336
337  @Override
338  public void copyValues(Base dst) {
339    super.copyValues(dst);
340
341    Element dest = (Element) dst;
342    if (comments != null) {
343      dest.comments = new ArrayList<>();
344      dest.comments.addAll(comments);
345    } else {
346      dest.comments = null;
347    }
348    dest.value = value;
349    if (children != null) {
350      dest.children = new ArrayList<>();
351      for (Element child : children) {
352        dest.children.add((Element) child.copy());
353      }
354    } else {
355      dest.children = null;
356    }
357    dest.line = line;
358    dest.col = col;
359    dest.xhtml = xhtml;
360    dest.explicitType = explicitType;
361  }
362
363  @Override
364  public Base setProperty(int hash, String name, Base value) throws FHIRException {
365    if ("xhtml".equals(getType()) && (hash == "value".hashCode())) {
366      this.xhtml = castToXhtml(value);
367      this.value = castToXhtmlString(value);
368      return this;
369    }
370    if (isPrimitive() && (hash == "value".hashCode())) {
371      this.value = castToString(value).asStringValue();
372      return this;
373    }
374
375    if (!value.isPrimitive() && !(value instanceof Element)) {
376      if (isDataType(value))
377        value = convertToElement(property.getChild(name), value);
378      else
379        throw new FHIRException("Cannot set property " + name + " on " + this.name
380            + " - value is not a primitive type (" + value.fhirType() + ") or an ElementModel type");
381    }
382
383    if (children == null)
384      children = new ArrayList<Element>();
385    Element childForValue = null;
386
387    // look through existing children
388    for (Element child : children) {
389      if (child.getName().equals(name)) {
390        if (!child.isList()) {
391          childForValue = child;
392          break;
393        } else {
394          Element ne = new Element(child);
395          children.add(ne);
396          numberChildren();
397          childForValue = ne;
398          break;
399        }
400      }
401    }
402
403    int i = 0;
404    if (childForValue == null)
405      for (Property p : property.getChildProperties(this.name, type)) {
406        int t = -1;
407        for (int c = 0; c < children.size(); c++) {
408          Element e = children.get(c);
409          if (p.getName().equals(e.getName()))
410            t = c;
411        }
412        if (t > i)
413          i = t;
414        if (p.getName().equals(name) || p.getName().equals(name + "[x]")) {
415          Element ne = new Element(name, p);
416          children.add(i, ne);
417          childForValue = ne;
418          break;
419        }
420      }
421
422    if (childForValue == null)
423      throw new Error("Cannot set property " + name + " on " + this.name);
424    else if (value.isPrimitive()) {
425      if (childForValue.property.getName().endsWith("[x]"))
426        childForValue.name = name + Utilities.capitalize(value.fhirType());
427      childForValue.setValue(value.primitiveValue());
428    } else {
429      Element ve = (Element) value;
430      childForValue.type = ve.getType();
431      if (childForValue.property.getName().endsWith("[x]"))
432        childForValue.name = name + Utilities.capitalize(childForValue.type);
433      else if (value.isResource()) {
434        if (childForValue.elementProperty == null)
435          childForValue.elementProperty = childForValue.property;
436        childForValue.property = ve.property;
437        childForValue.special = SpecialElement.BUNDLE_ENTRY;
438      }
439      if (ve.children != null) {
440        if (childForValue.children == null)
441          childForValue.children = new ArrayList<Element>();
442        else
443          childForValue.children.clear();
444        childForValue.children.addAll(ve.children);
445      }
446    }
447    return childForValue;
448  }
449
450  private Base convertToElement(Property prop, Base v) throws FHIRException {
451    return new ObjectConverter(property.getContext()).convert(prop, (Type) v);
452  }
453
454  private boolean isDataType(Base v) {
455    return v instanceof Type && property.getContext().getTypeNames().contains(v.fhirType());
456  }
457
458  @Override
459  public Base makeProperty(int hash, String name) throws FHIRException {
460    if (isPrimitive() && (hash == "value".hashCode())) {
461      return new StringType(value);
462    }
463
464    if (children == null)
465      children = new ArrayList<Element>();
466
467    // look through existing children
468    for (Element child : children) {
469      if (child.getName().equals(name)) {
470        if (!child.isList()) {
471          return child;
472        } else {
473          Element ne = new Element(child);
474          children.add(ne);
475          numberChildren();
476          return ne;
477        }
478      }
479    }
480
481    for (Property p : property.getChildProperties(this.name, type)) {
482      if (p.getName().equals(name)) {
483        Element ne = new Element(name, p);
484        children.add(ne);
485        return ne;
486      }
487    }
488
489    throw new Error("Unrecognised name " + name + " on " + this.name);
490  }
491
492  private int maxToInt(String max) {
493    if (max.equals("*"))
494      return Integer.MAX_VALUE;
495    else
496      return Integer.parseInt(max);
497  }
498
499  @Override
500  public boolean isPrimitive() {
501    return type != null ? property.isPrimitive(type) : property.isPrimitive(property.getType(name));
502  }
503
504  @Override
505  public boolean isBooleanPrimitive() {
506    return isPrimitive() && ("boolean".equals(type) || "boolean".equals(property.getType(name)));
507  }
508
509  @Override
510  public boolean isResource() {
511    return property.isResource();
512  }
513
514  @Override
515  public boolean hasPrimitiveValue() {
516    return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name);
517  }
518
519  @Override
520  public String primitiveValue() {
521    if (isPrimitive())
522      return value;
523    else {
524      if (hasPrimitiveValue() && children != null) {
525        for (Element c : children) {
526          if (c.getName().equals("value"))
527            return c.primitiveValue();
528        }
529      }
530      return null;
531    }
532  }
533
534  // for the validator
535  public int line() {
536    return line;
537  }
538
539  public int col() {
540    return col;
541  }
542
543  public Element markLocation(int line, int col) {
544    this.line = line;
545    this.col = col;
546    return this;
547  }
548
549  public void clearDecorations() {
550    clearUserData("fhir.decorations");
551    for (Element e : children)
552      e.clearDecorations();
553  }
554
555  public void markValidation(StructureDefinition profile, ElementDefinition definition) {
556    @SuppressWarnings("unchecked")
557    List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations");
558    if (decorations == null) {
559      decorations = new ArrayList<>();
560      setUserData("fhir.decorations", decorations);
561    }
562    decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath()));
563    if (definition.getId() != null && tail(definition.getId()).contains(":")) {
564      String[] details = tail(definition.getId()).split(":");
565      decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1]));
566    }
567  }
568
569  private String tail(String id) {
570    return id.contains(".") ? id.substring(id.lastIndexOf(".") + 1) : id;
571  }
572
573  public Element getNamedChild(String name) {
574    if (children == null)
575      return null;
576    Element result = null;
577    for (Element child : children) {
578      if (child.getName().equals(name)) {
579        if (result == null)
580          result = child;
581        else
582          throw new Error("Attempt to read a single element when there is more than one present (" + name + ")");
583      }
584    }
585    return result;
586  }
587
588  public void getNamedChildren(String name, List<Element> list) {
589    if (children != null)
590      for (Element child : children)
591        if (child.getName().equals(name))
592          list.add(child);
593  }
594
595  public String getNamedChildValue(String name) {
596    Element child = getNamedChild(name);
597    return child == null ? null : child.value;
598  }
599
600  public void getNamedChildrenWithWildcard(String string, List<Element> values) {
601    Validate.isTrue(string.endsWith("[x]"));
602
603    String start = string.substring(0, string.length() - 3);
604    if (children != null) {
605      for (Element child : children) {
606        if (child.getName().startsWith(start)) {
607          values.add(child);
608        }
609      }
610    }
611  }
612
613  public XhtmlNode getXhtml() {
614    return xhtml;
615  }
616
617  public Element setXhtml(XhtmlNode xhtml) {
618    this.xhtml = xhtml;
619    return this;
620  }
621
622  @Override
623  public boolean isEmpty() {
624    // GG: this used to also test !"".equals(value).
625    // the condition where "" is empty and there are no children is an error, and so
626    // this really only manifested as an issue in corner cases technical testing of
627    // the validator / FHIRPath.
628    // it should not cause any problems in real life.
629    if (value != null) {
630      return false;
631    }
632    for (Element next : getChildren()) {
633      if (!next.isEmpty()) {
634        return false;
635      }
636    }
637    return true;
638  }
639
640  public Property getElementProperty() {
641    return elementProperty;
642  }
643
644  public boolean hasElementProperty() {
645    return elementProperty != null;
646  }
647
648  public boolean hasChild(String name) {
649    return getNamedChild(name) != null;
650  }
651
652  public boolean hasChildren(String name) {
653    if (children != null)
654      for (Element child : children)
655        if (child.getName().equals(name))
656          return true;
657    return false;
658  }
659
660  @Override
661  public String toString() {
662    return name + "=" + fhirType() + "["
663        + (children == null || hasValue() ? value : Integer.toString(children.size()) + " children") + "]";
664  }
665
666  @Override
667  public String getIdBase() {
668    return getChildValue("id");
669  }
670
671  @Override
672  public void setIdBase(String value) {
673    setChildValue("id", value);
674  }
675
676  @Override
677  public boolean equalsDeep(Base other) {
678    if (!super.equalsDeep(other))
679      return false;
680    if (isPrimitive() && other.isPrimitive())
681      return primitiveValue().equals(other.primitiveValue());
682    if (isPrimitive() || other.isPrimitive())
683      return false;
684    Set<String> processed = new HashSet<String>();
685    for (org.hl7.fhir.r4.model.Property p : children()) {
686      String name = p.getName();
687      processed.add(name);
688      org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
689      if (!equalsDeep(p, o))
690        return false;
691    }
692    for (org.hl7.fhir.r4.model.Property p : children()) {
693      String name = p.getName();
694      if (!processed.contains(name)) {
695        org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
696        if (!equalsDeep(p, o))
697          return false;
698      }
699    }
700    return true;
701  }
702
703  private boolean equalsDeep(org.hl7.fhir.r4.model.Property p, org.hl7.fhir.r4.model.Property o) {
704    if (o == null || p == null)
705      return false;
706    if (p.getValues().size() != o.getValues().size())
707      return false;
708    for (int i = 0; i < p.getValues().size(); i++)
709      if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true))
710        return false;
711    return true;
712  }
713
714  @Override
715  public boolean equalsShallow(Base other) {
716    if (!super.equalsShallow(other))
717      return false;
718    if (isPrimitive() && other.isPrimitive())
719      return primitiveValue().equals(other.primitiveValue());
720    if (isPrimitive() || other.isPrimitive())
721      return false;
722    return true; // ?
723  }
724
725  public Type asType() throws FHIRException {
726    return new ObjectConverter(property.getContext()).convertToType(this);
727  }
728
729  @Override
730  public boolean isMetadataBased() {
731    return true;
732  }
733
734  public boolean isList() {
735    if (elementProperty != null)
736      return elementProperty.isList();
737    else
738      return property.isList();
739  }
740
741  @Override
742  public String[] getTypesForProperty(int hash, String name) throws FHIRException {
743    Property p = property.getChildSimpleName(this.name, name);
744    if (p != null) {
745      Set<String> types = new HashSet<String>();
746      for (TypeRefComponent tr : p.getDefinition().getType()) {
747        types.add(tr.getCode());
748      }
749      return types.toArray(new String[] {});
750    }
751    return super.getTypesForProperty(hash, name);
752
753  }
754
755  public void sort() {
756    if (children != null) {
757      List<Element> remove = new ArrayList<Element>();
758      for (Element child : children) {
759        child.sort();
760        if (child.isEmpty())
761          remove.add(child);
762      }
763      children.removeAll(remove);
764      Collections.sort(children, new ElementSortComparator(this, this.property));
765    }
766  }
767
768  public class ElementSortComparator implements Comparator<Element> {
769    private List<ElementDefinition> children;
770
771    public ElementSortComparator(Element e, Property property) {
772      String tn = e.getType();
773      StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class,
774          ProfileUtilities.sdNs(tn, property.getContext().getOverrideVersionNs()));
775      if (sd != null && !sd.getAbstract())
776        children = sd.getSnapshot().getElement();
777      else
778        children = property.getStructure().getSnapshot().getElement();
779    }
780
781    @Override
782    public int compare(Element e0, Element e1) {
783      int i0 = find(e0);
784      int i1 = find(e1);
785      return Integer.compare(i0, i1);
786    }
787
788    private int find(Element e0) {
789      int i = e0.elementProperty != null ? children.indexOf(e0.elementProperty.getDefinition())
790          : children.indexOf(e0.property.getDefinition());
791      return i;
792    }
793
794  }
795
796  public class ICodingImpl implements ICoding {
797    private String system;
798    private String version;
799    private String code;
800    private String display;
801    private boolean doesSystem;
802    private boolean doesVersion;
803    private boolean doesCode;
804    private boolean doesDisplay;
805
806    public ICodingImpl(boolean doesCode, boolean doesSystem, boolean doesVersion, boolean doesDisplay) {
807      super();
808      this.doesCode = doesCode;
809      this.doesSystem = doesSystem;
810      this.doesVersion = doesVersion;
811      this.doesDisplay = doesDisplay;
812    }
813
814    public String getSystem() {
815      return system;
816    }
817
818    public String getVersion() {
819      return version;
820    }
821
822    public String getCode() {
823      return code;
824    }
825
826    public String getDisplay() {
827      return display;
828    }
829
830    public boolean hasSystem() {
831      return !Utilities.noString(system);
832    }
833
834    public boolean hasVersion() {
835      return !Utilities.noString(version);
836    }
837
838    public boolean hasCode() {
839      return !Utilities.noString(code);
840    }
841
842    public boolean hasDisplay() {
843      return !Utilities.noString(display);
844    }
845
846    public boolean supportsSystem() {
847      return doesSystem;
848    }
849
850    public boolean supportsVersion() {
851      return doesVersion;
852    }
853
854    public boolean supportsCode() {
855      return doesCode;
856    }
857
858    public boolean supportsDisplay() {
859      return doesDisplay;
860    }
861  }
862
863  public ICoding getAsICoding() throws FHIRException {
864    if ("code".equals(fhirType())) {
865      if (property.getDefinition().getBinding().getStrength() != BindingStrength.REQUIRED)
866        return null;
867      ICodingImpl c = new ICodingImpl(true, true, false, false);
868      c.code = primitiveValue();
869      ValueSetExpansionOutcome vse = property.getContext().expandVS(property.getDefinition().getBinding(), true, false);
870      if (vse.getValueset() == null)
871        return null;
872      for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) {
873        if (cc.getCode().equals(c.code)) {
874          c.system = cc.getSystem();
875          if (cc.hasVersion()) {
876            c.doesVersion = true;
877            c.version = cc.getVersion();
878          }
879          if (cc.hasDisplay()) {
880            c.doesDisplay = true;
881            c.display = cc.getDisplay();
882          }
883        }
884      }
885      if (c.system == null)
886        return null;
887      return c;
888    } else if ("Coding".equals(fhirType())) {
889      ICodingImpl c = new ICodingImpl(true, true, true, true);
890      c.system = getNamedChildValue("system");
891      c.code = getNamedChildValue("code");
892      c.display = getNamedChildValue("display");
893      c.version = getNamedChildValue("version");
894      return c;
895    } else if ("Quantity".equals(fhirType())) {
896      ICodingImpl c = new ICodingImpl(true, true, false, false);
897      c.system = getNamedChildValue("system");
898      c.code = getNamedChildValue("code");
899      return c;
900    } else
901      return null;
902  }
903
904  public String getExplicitType() {
905    return explicitType;
906  }
907
908  public void setExplicitType(String explicitType) {
909    this.explicitType = explicitType;
910  }
911
912}