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