001package org.hl7.fhir.dstu3.model;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.HashSet;
006import java.util.List;
007import java.util.Set;
008
009import org.hl7.fhir.dstu3.context.IWorkerContext;
010import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionBindingComponent;
011import org.hl7.fhir.dstu3.model.ExpressionNode.CollectionStatus;
012import org.hl7.fhir.exceptions.DefinitionException;
013import org.hl7.fhir.utilities.Utilities;
014
015public class TypeDetails {
016  public static class ProfiledType {
017    private String uri;
018    private List<String> profiles; // or, not and
019    private List<ElementDefinitionBindingComponent> bindings;
020    
021    public ProfiledType(String n) {
022      uri = ns(n);    
023    }
024    
025    public String getUri() {
026      return uri;
027    }
028
029    public boolean hasProfiles() {
030      return profiles != null && profiles.size() > 0;
031    }
032    public List<String> getProfiles() {
033      return profiles;
034    }
035
036    public boolean hasBindings() {
037      return bindings != null && bindings.size() > 0;
038    }
039    public List<ElementDefinitionBindingComponent> getBindings() {
040      return bindings;
041    }
042
043    public static String ns(String n) {
044      return Utilities.isAbsoluteUrl(n) ? n : "http://hl7.org/fhir/StructureDefinition/"+n;
045    }
046
047    public void addProfile(String profile) {
048      profiles = new ArrayList<String>();
049      profiles.add(profile);
050    }
051
052    public void addBinding(ElementDefinitionBindingComponent binding) {
053      bindings = new ArrayList<ElementDefinitionBindingComponent>();
054      bindings.add(binding);
055    }
056
057    public boolean hasBinding(ElementDefinitionBindingComponent b) {
058      return false; // todo: do we need to do this?
059    }
060  }
061  
062  private List<ProfiledType> types = new ArrayList<ProfiledType>();
063  private CollectionStatus collectionStatus;
064  public TypeDetails(CollectionStatus collectionStatus, String... names) {
065    super();
066    this.collectionStatus = collectionStatus;
067    for (String n : names) {
068      this.types.add(new ProfiledType(n));
069    }
070  }
071  public TypeDetails(CollectionStatus collectionStatus, Set<String> names) {
072    super();
073    this.collectionStatus = collectionStatus;
074    for (String n : names) {
075      addType(new ProfiledType(n));
076    }
077  }
078  public TypeDetails(CollectionStatus collectionStatus, ProfiledType pt) {
079    super();
080    this.collectionStatus = collectionStatus;
081    this.types.add(pt);
082  }
083  public String addType(String n) {
084    ProfiledType pt = new ProfiledType(n);
085    String res = pt.uri;
086    addType(pt);
087    return res;
088  }
089  public String addType(String n, String p) {
090    ProfiledType pt = new ProfiledType(n);
091    pt.addProfile(p);
092    String res = pt.uri;
093    addType(pt);
094    return res;
095  }
096  public void addType(ProfiledType pt) {
097    for (ProfiledType et : types) {
098      if (et.uri.equals(pt.uri)) {
099        if (pt.profiles != null) {
100          for (String p : pt.profiles) {
101            if (et.profiles == null)
102              et.profiles = new ArrayList<String>();
103            if (!et.profiles.contains(p))
104              et.profiles.add(p);
105          }
106        }
107        if (pt.bindings != null) {
108          for (ElementDefinitionBindingComponent b : pt.bindings) {
109            if (et.bindings == null)
110              et.bindings = new ArrayList<ElementDefinitionBindingComponent>();
111            if (!et.hasBinding(b))
112              et.bindings.add(b);
113          }
114        }
115        return;
116      }
117    }
118    types.add(pt); 
119  }
120  
121  public void addTypes(Collection<String> names) {
122    for (String n : names) 
123      addType(new ProfiledType(n));
124  }
125  
126  public boolean hasType(IWorkerContext context, String... tn) {
127    for (String n: tn) {
128      String t = ProfiledType.ns(n);
129      if (typesContains(t))
130        return true;
131    }
132    for (String n: tn) {
133      String id = n.contains("#") ? n.substring(0, n.indexOf("#")) : n;
134      String tail = null;
135      if (n.contains("#")) {
136        tail = n.substring( n.indexOf("#")+1);
137        tail = tail.substring(tail.indexOf("."));
138      }
139      String t = ProfiledType.ns(n);
140      StructureDefinition sd = context.fetchResource(StructureDefinition.class, t);
141      while (sd != null) {
142        if (tail == null && typesContains(sd.getUrl()))
143            return true;
144        if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
145          return true;
146        if (sd.hasBaseDefinition())
147          sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
148        else
149          sd = null;
150      }
151    }
152    return false;
153  }
154  
155  private boolean typesContains(String t) {
156    for (ProfiledType pt : types)
157      if (pt.uri.equals(t))
158        return true;
159    return false;
160  }
161  
162  public void update(TypeDetails source) {
163    for (ProfiledType pt : source.types)
164      addType(pt);
165    if (collectionStatus == null)
166      collectionStatus = source.collectionStatus;
167    else if (source.collectionStatus == CollectionStatus.UNORDERED)
168      collectionStatus = source.collectionStatus;
169    else
170      collectionStatus = CollectionStatus.ORDERED;
171  }
172  public TypeDetails union(TypeDetails right) {
173    TypeDetails result = new TypeDetails(null);
174    if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED)
175      result.collectionStatus = CollectionStatus.UNORDERED;
176    else 
177      result.collectionStatus = CollectionStatus.ORDERED;
178    for (ProfiledType pt : types)
179      result.addType(pt);
180    for (ProfiledType pt : right.types)
181      result.addType(pt);
182    return result;
183  }
184  
185  public boolean hasNoTypes() {
186    return types.isEmpty();
187  }
188  public Set<String> getTypes() {
189    Set<String> res = new HashSet<String>();
190    for (ProfiledType pt : types)
191      res.add(pt.uri);
192    return res;
193  }
194  public TypeDetails toSingleton() {
195    TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON);
196    result.types.addAll(types);
197    return result;
198  }
199  public CollectionStatus getCollectionStatus() {
200    return collectionStatus;
201  }
202  public boolean hasType(Set<String> tn) {
203    for (String n: tn) {
204      String t = ProfiledType.ns(n);
205      if (typesContains(t))
206        return true;
207    }
208    return false;
209  }
210  public String describe() {
211    return getTypes().toString();
212  }
213  public String getType() {
214    for (ProfiledType pt : types)
215      return pt.uri;
216    return null;
217  }
218  @Override
219  public String toString() {
220    return (collectionStatus == null ? collectionStatus.SINGLETON.toString() : collectionStatus.toString()) + getTypes().toString();
221  }
222  public String getTypeCode() throws DefinitionException {
223    if (types.size() != 1)
224      throw new DefinitionException("Multiple types? ("+types.toString()+")");
225    for (ProfiledType pt : types)
226      if (pt.uri.startsWith("http://hl7.org/fhir/StructureDefinition/"))
227        return pt.uri.substring(40);
228      else
229        return pt.uri;
230    return null;
231  }
232  public List<ProfiledType> getProfiledTypes() {
233    return types;
234  }
235  public boolean hasBinding() {
236    for (ProfiledType pt : types) {
237      if (pt.hasBindings())
238        return true;
239    }
240    return false;
241  }
242  public ElementDefinitionBindingComponent getBinding() {
243    for (ProfiledType pt : types) {
244      for (ElementDefinitionBindingComponent b : pt.getBindings())
245        return b;
246    }
247    return null;
248  }
249}