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
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.r4.conformance.ProfileUtilities;
045import org.hl7.fhir.r4.model.Base;
046import org.hl7.fhir.r4.model.ElementDefinition;
047import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
048import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
049import org.hl7.fhir.r4.model.ICoding;
050import org.hl7.fhir.r4.model.StringType;
051import org.hl7.fhir.r4.model.StructureDefinition;
052import org.hl7.fhir.r4.model.Type;
053import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
054import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
055import org.hl7.fhir.utilities.ElementDecoration;
056import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
057import org.hl7.fhir.utilities.Utilities;
058import org.hl7.fhir.utilities.xhtml.XhtmlNode;
059
060/**
061 * This class represents the underlying reference model of FHIR
062 * 
063 * A resource is nothing but a set of elements, where every element has a 
064 * name, maybe a stated type, maybe an id, and either a value or child elements 
065 * (one or the other, but not both or neither)
066 * 
067 * @author Grahame Grieve
068 *
069 */
070public class Element extends Base {
071
072
073  public enum SpecialElement {
074                CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER;
075
076    public static SpecialElement fromProperty(Property property) {
077      if (property.getStructure().getIdElement().getIdPart().equals("Parameters"))
078        return PARAMETER;
079      if (property.getStructure().getIdElement().getIdPart().equals("Bundle") && property.getName().equals("resource"))
080        return BUNDLE_ENTRY;
081      if (property.getStructure().getIdElement().getIdPart().equals("Bundle") && property.getName().equals("outcome"))
082        return BUNDLE_OUTCOME;
083      if (property.getName().equals("contained")) 
084        return CONTAINED;
085      throw new Error("Unknown resource containing a native resource: "+property.getDefinition().getId());
086    }
087        }
088
089        private List<String> comments;// not relevant for production, but useful in documentation
090        private String name;
091        private String type;
092        private String value;
093        private int index = -1;
094        private List<Element> children;
095        private Property property;
096  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
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() && 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(), c.getProperty().getDefinition().getDefinition(), c.getProperty().getDefinition().getMin(), maxToInt(c.getProperty().getDefinition().getMax()), c);
319          childProps.add(p);
320          map.put(c.getName(), p);
321              
322              } else
323                p.getValues().add(c);
324            }
325          }
326        }
327        
328        @Override
329  public Base copy() {
330    Element element = new Element(this);
331    this.copyValues(element);
332    return element;
333  }
334
335  @Override
336  public void copyValues(Base dst) {
337    super.copyValues(dst);
338    
339    Element dest = (Element) dst;
340    if (comments != null) {
341      dest.comments = new ArrayList<>();
342      dest.comments.addAll(comments);
343    } else {
344      dest.comments = null;
345    }
346    dest.value = value;
347    if (children != null) {
348      dest.children = new ArrayList<>();
349      for (Element child : children) {
350        dest.children.add((Element) child.copy());
351      }
352    } else {
353      dest.children = null;
354    }
355    dest.line = line;
356    dest.col = col;
357    dest.xhtml = xhtml;
358    dest.explicitType = explicitType;
359  }
360  
361  
362  @Override
363  public Base setProperty(int hash, String name, Base value) throws FHIRException {
364    if ("xhtml".equals(getType()) && (hash == "value".hashCode())) {
365      this.xhtml = castToXhtml(value);
366      this.value =  castToXhtmlString(value);
367      return this;
368    }
369    if (isPrimitive() && (hash == "value".hashCode())) {
370      this.value = castToString(value).asStringValue();
371      return this;
372    }
373    
374    if (!value.isPrimitive() && !(value instanceof Element)) {
375      if (isDataType(value)) 
376        value = convertToElement(property.getChild(name), value);
377      else
378        throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type");
379    }
380    
381    if (children == null)
382      children = new ArrayList<Element>();
383    Element childForValue = null;
384    
385    // look through existing children
386    for (Element child : children) {
387      if (child.getName().equals(name)) {
388        if (!child.isList()) {
389          childForValue = child;
390          break;
391        } else {
392          Element ne = new Element(child);
393          children.add(ne);
394          numberChildren();
395          childForValue = ne;
396          break;
397        }
398      }
399    }
400
401    int i = 0;
402    if (childForValue == null)
403      for (Property p : property.getChildProperties(this.name, type)) {
404        int t = -1;
405        for (int c =0; c < children.size(); c++) {
406          Element e = children.get(c);
407          if (p.getName().equals(e.getName()))
408            t = c;
409        }
410        if (t > i)
411          i = t;
412        if (p.getName().equals(name) || p.getName().equals(name+"[x]")) {
413          Element ne = new Element(name, p);
414          children.add(i, ne);
415          childForValue = ne;
416          break;
417        }
418      }
419    
420    if (childForValue == null)
421      throw new Error("Cannot set property "+name+" on "+this.name);
422    else if (value.isPrimitive()) {
423      if (childForValue.property.getName().endsWith("[x]"))
424        childForValue.name = name+Utilities.capitalize(value.fhirType());
425      childForValue.setValue(value.primitiveValue());
426    } else {
427      Element ve = (Element) value;
428      childForValue.type = ve.getType();
429      if (childForValue.property.getName().endsWith("[x]"))
430        childForValue.name = name+Utilities.capitalize(childForValue.type);
431      else if (value.isResource()) {
432        if (childForValue.elementProperty == null)
433          childForValue.elementProperty = childForValue.property;
434        childForValue.property = ve.property;
435        childForValue.special = SpecialElement.BUNDLE_ENTRY;
436      }
437      if (ve.children != null) {
438        if (childForValue.children == null)
439          childForValue.children = new ArrayList<Element>();
440        else 
441          childForValue.children.clear();
442        childForValue.children.addAll(ve.children);
443      }
444    }
445    return childForValue;
446  }
447
448  private Base convertToElement(Property prop, Base v) throws FHIRException {
449    return new ObjectConverter(property.getContext()).convert(prop, (Type) v);
450  }
451
452  private boolean isDataType(Base v) {
453    return v instanceof Type &&  property.getContext().getTypeNames().contains(v.fhirType());
454  }
455
456  @Override
457  public Base makeProperty(int hash, String name) throws FHIRException {
458    if (isPrimitive() && (hash == "value".hashCode())) {
459      return new StringType(value);
460    }
461
462    if (children == null)
463      children = new ArrayList<Element>();
464    
465    // look through existing children
466    for (Element child : children) {
467      if (child.getName().equals(name)) {
468        if (!child.isList()) {
469          return child;
470        } else {
471          Element ne = new Element(child);
472          children.add(ne);
473          numberChildren();
474          return ne;
475        }
476      }
477    }
478
479    for (Property p : property.getChildProperties(this.name, type)) {
480      if (p.getName().equals(name)) {
481        Element ne = new Element(name, p);
482        children.add(ne);
483        return ne;
484      }
485    }
486      
487    throw new Error("Unrecognised name "+name+" on "+this.name); 
488  }
489  
490        private int maxToInt(String max) {
491    if (max.equals("*"))
492      return Integer.MAX_VALUE;
493    else
494      return Integer.parseInt(max);
495        }
496
497        @Override
498        public boolean isPrimitive() {
499                return type != null ? property.isPrimitive(type) : property.isPrimitive(property.getType(name));
500        }
501        
502  @Override
503  public boolean isBooleanPrimitive() {
504    return isPrimitive() && ("boolean".equals(type) || "boolean".equals(property.getType(name)));
505  }
506 
507  @Override
508  public boolean isResource() {
509    return property.isResource();
510  }
511  
512
513        @Override
514        public boolean hasPrimitiveValue() {
515                return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name);
516        }
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  
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 this really only manifested as an issue in corner cases technical testing of the validator / FHIRPath.
627          // it should not cause any problems in real life.
628                if (value != null) {   
629                        return false;
630                }
631                for (Element next : getChildren()) {
632                        if (!next.isEmpty()) {
633                                return false;
634                        }
635                }
636                return true;
637        }
638
639  public Property getElementProperty() {
640    return elementProperty;
641  }
642
643  public boolean hasElementProperty() {
644    return elementProperty != null;
645  }
646
647  public boolean hasChild(String name) {
648    return getNamedChild(name) != null;
649  }
650
651  public boolean hasChildren(String name) {
652    if (children != null)
653      for (Element child : children) 
654        if (child.getName().equals(name))
655          return true;
656    return false;
657  }
658
659  @Override
660  public String toString() {
661    return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
662  }
663
664  @Override
665  public String getIdBase() {
666    return getChildValue("id");
667  }
668
669  @Override
670  public void setIdBase(String value) {
671    setChildValue("id", value);
672  }
673
674
675  @Override
676  public boolean equalsDeep(Base other) {
677    if (!super.equalsDeep(other))
678      return false;
679    if (isPrimitive() && other.isPrimitive())
680      return primitiveValue().equals(other.primitiveValue());
681    if (isPrimitive() || other.isPrimitive())
682      return false;
683    Set<String> processed  = new HashSet<String>();
684    for (org.hl7.fhir.r4.model.Property p : children()) {
685      String name = p.getName();
686      processed.add(name);
687      org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
688      if (!equalsDeep(p, o))
689        return false;
690    }
691    for (org.hl7.fhir.r4.model.Property p : children()) {
692      String name = p.getName();
693      if (!processed.contains(name)) {
694        org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
695        if (!equalsDeep(p, o))
696          return false;
697      }
698    }
699    return true;
700  }
701
702  private boolean equalsDeep(org.hl7.fhir.r4.model.Property p, org.hl7.fhir.r4.model.Property o) {
703    if (o == null || p == null)
704      return false;
705    if (p.getValues().size() != o.getValues().size())
706      return false;
707    for (int i = 0; i < p.getValues().size(); i++)
708      if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true))
709        return false;
710    return true;
711  }
712
713  @Override
714  public boolean equalsShallow(Base other) {
715    if (!super.equalsShallow(other))
716      return false;
717    if (isPrimitive() && other.isPrimitive())
718      return primitiveValue().equals(other.primitiveValue());
719    if (isPrimitive() || other.isPrimitive())
720      return false;
721    return true; //?
722  }
723
724  public Type asType() throws FHIRException {
725    return new ObjectConverter(property.getContext()).convertToType(this);
726  }
727
728  @Override
729  public boolean isMetadataBased() {
730    return true;
731  }
732
733  public boolean isList() {
734    if (elementProperty != null)
735      return elementProperty.isList();
736    else
737      return property.isList();
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  
895}