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 setProperty(int hash, String name, Base value) throws FHIRException {
330    if ("xhtml".equals(getType()) && (hash == "value".hashCode())) {
331      this.xhtml = castToXhtml(value);
332      this.value =  castToXhtmlString(value);
333      return this;
334    }
335    if (isPrimitive() && (hash == "value".hashCode())) {
336      this.value = castToString(value).asStringValue();
337      return this;
338    }
339    
340    if (!value.isPrimitive() && !(value instanceof Element)) {
341      if (isDataType(value)) 
342        value = convertToElement(property.getChild(name), value);
343      else
344        throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type");
345    }
346    
347    if (children == null)
348      children = new ArrayList<Element>();
349    Element childForValue = null;
350    
351    // look through existing children
352    for (Element child : children) {
353      if (child.getName().equals(name)) {
354        if (!child.isList()) {
355          childForValue = child;
356          break;
357        } else {
358          Element ne = new Element(child);
359          children.add(ne);
360          numberChildren();
361          childForValue = ne;
362          break;
363        }
364      }
365    }
366
367    int i = 0;
368    if (childForValue == null)
369      for (Property p : property.getChildProperties(this.name, type)) {
370        int t = -1;
371        for (int c =0; c < children.size(); c++) {
372          Element e = children.get(c);
373          if (p.getName().equals(e.getName()))
374            t = c;
375        }
376        if (t > i)
377          i = t;
378        if (p.getName().equals(name) || p.getName().equals(name+"[x]")) {
379          Element ne = new Element(name, p);
380          children.add(i, ne);
381          childForValue = ne;
382          break;
383        }
384      }
385    
386    if (childForValue == null)
387      throw new Error("Cannot set property "+name+" on "+this.name);
388    else if (value.isPrimitive()) {
389      if (childForValue.property.getName().endsWith("[x]"))
390        childForValue.name = name+Utilities.capitalize(value.fhirType());
391      childForValue.setValue(value.primitiveValue());
392    } else {
393      Element ve = (Element) value;
394      childForValue.type = ve.getType();
395      if (childForValue.property.getName().endsWith("[x]"))
396        childForValue.name = name+Utilities.capitalize(childForValue.type);
397      else if (value.isResource()) {
398        if (childForValue.elementProperty == null)
399          childForValue.elementProperty = childForValue.property;
400        childForValue.property = ve.property;
401        childForValue.special = SpecialElement.BUNDLE_ENTRY;
402      }
403      if (ve.children != null) {
404        if (childForValue.children == null)
405          childForValue.children = new ArrayList<Element>();
406        else 
407          childForValue.children.clear();
408        childForValue.children.addAll(ve.children);
409      }
410    }
411    return childForValue;
412  }
413
414  private Base convertToElement(Property prop, Base v) throws FHIRException {
415    return new ObjectConverter(property.getContext()).convert(prop, (Type) v);
416  }
417
418  private boolean isDataType(Base v) {
419    return v instanceof Type &&  property.getContext().getTypeNames().contains(v.fhirType());
420  }
421
422  @Override
423  public Base makeProperty(int hash, String name) throws FHIRException {
424    if (isPrimitive() && (hash == "value".hashCode())) {
425      return new StringType(value);
426    }
427
428    if (children == null)
429      children = new ArrayList<Element>();
430    
431    // look through existing children
432    for (Element child : children) {
433      if (child.getName().equals(name)) {
434        if (!child.isList()) {
435          return child;
436        } else {
437          Element ne = new Element(child);
438          children.add(ne);
439          numberChildren();
440          return ne;
441        }
442      }
443    }
444
445    for (Property p : property.getChildProperties(this.name, type)) {
446      if (p.getName().equals(name)) {
447        Element ne = new Element(name, p);
448        children.add(ne);
449        return ne;
450      }
451    }
452      
453    throw new Error("Unrecognised name "+name+" on "+this.name); 
454  }
455  
456        private int maxToInt(String max) {
457    if (max.equals("*"))
458      return Integer.MAX_VALUE;
459    else
460      return Integer.parseInt(max);
461        }
462
463        @Override
464        public boolean isPrimitive() {
465                return type != null ? property.isPrimitive(type) : property.isPrimitive(property.getType(name));
466        }
467        
468  @Override
469  public boolean isBooleanPrimitive() {
470    return isPrimitive() && ("boolean".equals(type) || "boolean".equals(property.getType(name)));
471  }
472 
473  @Override
474  public boolean isResource() {
475    return property.isResource();
476  }
477  
478
479        @Override
480        public boolean hasPrimitiveValue() {
481                return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name);
482        }
483        
484
485        @Override
486        public String primitiveValue() {
487                if (isPrimitive())
488                  return value;
489                else {
490                        if (hasPrimitiveValue() && children != null) {
491                                for (Element c : children) {
492                                        if (c.getName().equals("value"))
493                                                return c.primitiveValue();
494                                }
495                        }
496                        return null;
497                }
498        }
499        
500        // for the validator
501  public int line() {
502    return line;
503  }
504
505  public int col() {
506    return col;
507  }
508
509        public Element markLocation(int line, int col) {
510                this.line = line;
511                this.col = col; 
512                return this;
513        }
514
515        public void clearDecorations() {
516          clearUserData("fhir.decorations");
517          for (Element e : children)
518            e.clearDecorations();         
519        }
520        
521        public void markValidation(StructureDefinition profile, ElementDefinition definition) {
522          @SuppressWarnings("unchecked")
523    List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations");
524          if (decorations == null) {
525            decorations = new ArrayList<>();
526            setUserData("fhir.decorations", decorations);
527          }
528          decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath()));
529          if (definition.getId() != null && tail(definition.getId()).contains(":")) {
530            String[] details = tail(definition.getId()).split(":");
531            decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1]));
532          }
533        }
534        
535  private String tail(String id) {
536    return id.contains(".") ? id.substring(id.lastIndexOf(".")+1) : id;
537  }
538
539  public Element getNamedChild(String name) {
540          if (children == null)
541                return null;
542          Element result = null;
543          for (Element child : children) {
544                if (child.getName().equals(name)) {
545                        if (result == null)
546                                result = child;
547                        else 
548                                throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
549                }
550          }
551          return result;
552        }
553
554  public void getNamedChildren(String name, List<Element> list) {
555        if (children != null)
556                for (Element child : children) 
557                        if (child.getName().equals(name))
558                                list.add(child);
559  }
560
561  public String getNamedChildValue(String name) {
562        Element child = getNamedChild(name);
563        return child == null ? null : child.value;
564  }
565
566  public void getNamedChildrenWithWildcard(String string, List<Element> values) {
567          Validate.isTrue(string.endsWith("[x]"));
568          
569          String start = string.substring(0, string.length() - 3);
570                if (children != null) {
571                        for (Element child : children) { 
572                                if (child.getName().startsWith(start)) {
573                                        values.add(child);
574                                }
575                        }
576                }
577  }
578
579  
580        public XhtmlNode getXhtml() {
581                return xhtml;
582        }
583
584        public Element setXhtml(XhtmlNode xhtml) {
585                this.xhtml = xhtml;
586                return this;
587        }
588
589        @Override
590        public boolean isEmpty() {
591        // GG: this used to also test !"".equals(value). 
592    // 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.
593          // it should not cause any problems in real life.
594                if (value != null) {   
595                        return false;
596                }
597                for (Element next : getChildren()) {
598                        if (!next.isEmpty()) {
599                                return false;
600                        }
601                }
602                return true;
603        }
604
605  public Property getElementProperty() {
606    return elementProperty;
607  }
608
609  public boolean hasElementProperty() {
610    return elementProperty != null;
611  }
612
613  public boolean hasChild(String name) {
614    return getNamedChild(name) != null;
615  }
616
617  public boolean hasChildren(String name) {
618    if (children != null)
619      for (Element child : children) 
620        if (child.getName().equals(name))
621          return true;
622    return false;
623  }
624
625  @Override
626  public String toString() {
627    return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
628  }
629
630  @Override
631  public String getIdBase() {
632    return getChildValue("id");
633  }
634
635  @Override
636  public void setIdBase(String value) {
637    setChildValue("id", value);
638  }
639
640
641  @Override
642  public boolean equalsDeep(Base other) {
643    if (!super.equalsDeep(other))
644      return false;
645    if (isPrimitive() && other.isPrimitive())
646      return primitiveValue().equals(other.primitiveValue());
647    if (isPrimitive() || other.isPrimitive())
648      return false;
649    Set<String> processed  = new HashSet<String>();
650    for (org.hl7.fhir.r4.model.Property p : children()) {
651      String name = p.getName();
652      processed.add(name);
653      org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
654      if (!equalsDeep(p, o))
655        return false;
656    }
657    for (org.hl7.fhir.r4.model.Property p : children()) {
658      String name = p.getName();
659      if (!processed.contains(name)) {
660        org.hl7.fhir.r4.model.Property o = other.getChildByName(name);
661        if (!equalsDeep(p, o))
662          return false;
663      }
664    }
665    return true;
666  }
667
668  private boolean equalsDeep(org.hl7.fhir.r4.model.Property p, org.hl7.fhir.r4.model.Property o) {
669    if (o == null || p == null)
670      return false;
671    if (p.getValues().size() != o.getValues().size())
672      return false;
673    for (int i = 0; i < p.getValues().size(); i++)
674      if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true))
675        return false;
676    return true;
677  }
678
679  @Override
680  public boolean equalsShallow(Base other) {
681    if (!super.equalsShallow(other))
682      return false;
683    if (isPrimitive() && other.isPrimitive())
684      return primitiveValue().equals(other.primitiveValue());
685    if (isPrimitive() || other.isPrimitive())
686      return false;
687    return true; //?
688  }
689
690  public Type asType() throws FHIRException {
691    return new ObjectConverter(property.getContext()).convertToType(this);
692  }
693
694  @Override
695  public boolean isMetadataBased() {
696    return true;
697  }
698
699  public boolean isList() {
700    if (elementProperty != null)
701      return elementProperty.isList();
702    else
703      return property.isList();
704  }
705  
706  @Override
707  public String[] getTypesForProperty(int hash, String name) throws FHIRException {
708    Property p = property.getChildSimpleName(this.name, name);
709    if (p != null) {
710      Set<String> types = new HashSet<String>();
711      for (TypeRefComponent tr : p.getDefinition().getType()) {
712        types.add(tr.getCode());
713      }
714      return types.toArray(new String[]{});
715    }
716    return super.getTypesForProperty(hash, name);
717
718  }
719
720  public void sort() {
721    if (children != null) {
722      List<Element> remove = new ArrayList<Element>();
723      for (Element child : children) {
724        child.sort();
725        if (child.isEmpty())
726          remove.add(child);
727      }
728      children.removeAll(remove);
729      Collections.sort(children, new ElementSortComparator(this, this.property));
730    }
731  }
732
733  public class ElementSortComparator implements Comparator<Element> {
734    private List<ElementDefinition> children;
735    public ElementSortComparator(Element e, Property property) {
736      String tn = e.getType();
737      StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn, property.getContext().getOverrideVersionNs()));
738      if (sd != null && !sd.getAbstract())
739        children = sd.getSnapshot().getElement();
740      else
741        children = property.getStructure().getSnapshot().getElement();
742    }
743    
744    @Override
745    public int compare(Element e0, Element e1) {
746      int i0 = find(e0);
747      int i1 = find(e1);
748      return Integer.compare(i0, i1);
749    }
750    private int find(Element e0) {
751      int i =  e0.elementProperty != null ? children.indexOf(e0.elementProperty.getDefinition()) :  children.indexOf(e0.property.getDefinition());
752      return i; 
753    }
754
755  }
756
757  public class ICodingImpl implements ICoding {
758    private String system;
759    private String version;
760    private String code;
761    private String display;
762    private boolean doesSystem;
763    private boolean doesVersion;
764    private boolean doesCode;
765    private boolean doesDisplay;
766    public ICodingImpl(boolean doesCode, boolean doesSystem, boolean doesVersion, boolean doesDisplay) {
767      super();
768      this.doesCode = doesCode;
769      this.doesSystem = doesSystem;
770      this.doesVersion = doesVersion;
771      this.doesDisplay = doesDisplay;
772    }
773    public String getSystem() {
774      return system;
775    }
776    public String getVersion() {
777      return version;
778    }
779    public String getCode() {
780      return code;
781    }
782    public String getDisplay() {
783      return display;
784    }
785    public boolean hasSystem() {
786      return !Utilities.noString(system); 
787    }
788    public boolean hasVersion() {
789      return !Utilities.noString(version);
790    }
791    public boolean hasCode() {
792      return !Utilities.noString(code);
793    }
794    public boolean hasDisplay() {
795      return !Utilities.noString(display);
796    }
797    public boolean supportsSystem() {
798      return doesSystem;
799    }
800    public boolean supportsVersion() {
801      return doesVersion;
802    }
803    public boolean supportsCode() {
804      return doesCode;
805    }
806    public boolean supportsDisplay() {
807      return doesDisplay;
808    }    
809  }
810
811  public ICoding getAsICoding() throws FHIRException {
812    if ("code".equals(fhirType())) {
813      if (property.getDefinition().getBinding().getStrength() != BindingStrength.REQUIRED)
814        return null;
815      ICodingImpl c = new ICodingImpl(true, true, false, false);
816      c.code = primitiveValue();
817      ValueSetExpansionOutcome vse = property.getContext().expandVS(property.getDefinition().getBinding(), true, false);
818      if (vse.getValueset() == null)
819        return null;
820      for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) {
821        if (cc.getCode().equals(c.code)) {
822          c.system = cc.getSystem();
823          if (cc.hasVersion()) {
824            c.doesVersion = true;
825            c.version = cc.getVersion();
826          }
827          if (cc.hasDisplay()) {
828            c.doesDisplay = true;
829            c.display = cc.getDisplay();
830          }
831        }
832      }
833      if (c.system == null)
834        return null;
835      return c;   
836    } else if ("Coding".equals(fhirType())) {
837      ICodingImpl c = new ICodingImpl(true, true, true, true);
838      c.system = getNamedChildValue("system");
839      c.code = getNamedChildValue("code");
840      c.display = getNamedChildValue("display");
841      c.version = getNamedChildValue("version");
842      return c;
843    } else if ("Quantity".equals(fhirType())) {
844      ICodingImpl c = new ICodingImpl(true, true, false, false);
845      c.system = getNamedChildValue("system");
846      c.code = getNamedChildValue("code");
847      return c;
848    } else 
849      return null;
850  }
851
852  public String getExplicitType() {
853    return explicitType;
854  }
855
856  public void setExplicitType(String explicitType) {
857    this.explicitType = explicitType;
858  }
859
860  
861}