001package org.hl7.fhir.r4.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.r4.context.IWorkerContext;
010import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
011import org.hl7.fhir.r4.model.ExpressionNode.CollectionStatus;
012import org.hl7.fhir.exceptions.DefinitionException;
013import org.hl7.fhir.utilities.Utilities;
014
015
016
017public class TypeDetails {
018  public static final String FHIR_NS = "http://hl7.org/fhir/StructureDefinition/";
019  public static final String FP_NS = "http://hl7.org/fhirpath/";
020  public static final String FP_String = "http://hl7.org/fhirpath/String";
021  public static final String FP_Boolean = "http://hl7.org/fhirpath/Boolean";
022  public static final String FP_Integer = "http://hl7.org/fhirpath/Integer";
023  public static final String FP_Decimal = "http://hl7.org/fhirpath/Decimal";
024  public static final String FP_Quantity = "http://hl7.org/fhirpath/Quantity";
025  public static final String FP_DateTime = "http://hl7.org/fhirpath/DateTime";
026  public static final String FP_Time = "http://hl7.org/fhirpath/Time";
027  public static final String FP_SimpleTypeInfo = "http://hl7.org/fhirpath/SimpleTypeInfo";
028  public static final String FP_ClassInfo = "http://hl7.org/fhirpath/ClassInfo";
029
030  public static class ProfiledType {
031    private String uri;
032    private List<String> profiles; // or, not and
033    private List<ElementDefinitionBindingComponent> bindings;
034    
035    public ProfiledType(String n) {
036      uri = ns(n);    
037    }
038    
039    public String getUri() {
040      return uri;
041    }
042
043    public boolean hasProfiles() {
044      return profiles != null && profiles.size() > 0;
045    }
046    public List<String> getProfiles() {
047      return profiles;
048    }
049
050    public boolean hasBindings() {
051      return bindings != null && bindings.size() > 0;
052    }
053    public List<ElementDefinitionBindingComponent> getBindings() {
054      return bindings;
055    }
056
057    public static String ns(String n) {
058      return Utilities.isAbsoluteUrl(n) ? n : FHIR_NS+n;
059    }
060
061    public void addProfile(String profile) {
062      if (profiles == null)
063        profiles = new ArrayList<String>();
064      profiles.add(profile);
065    }
066
067    public void addBinding(ElementDefinitionBindingComponent binding) {
068      bindings = new ArrayList<ElementDefinitionBindingComponent>();
069      bindings.add(binding);
070    }
071
072    public boolean hasBinding(ElementDefinitionBindingComponent b) {
073      return false; // todo: do we need to do this?
074    }
075
076    public void addProfiles(List<CanonicalType> list) {
077      if (profiles == null)
078        profiles = new ArrayList<String>();
079      for (UriType u : list)
080        profiles.add(u.getValue());
081    }
082    public boolean isSystemType() {
083      return uri.startsWith(FP_NS);
084    }
085  }
086  
087  private List<ProfiledType> types = new ArrayList<ProfiledType>();
088  private CollectionStatus collectionStatus;
089  public TypeDetails(CollectionStatus collectionStatus, String... names) {
090    super();
091    this.collectionStatus = collectionStatus;
092    for (String n : names) {
093      this.types.add(new ProfiledType(n));
094    }
095  }
096  public TypeDetails(CollectionStatus collectionStatus, Set<String> names) {
097    super();
098    this.collectionStatus = collectionStatus;
099    for (String n : names) {
100      addType(new ProfiledType(n));
101    }
102  }
103  public TypeDetails(CollectionStatus collectionStatus, ProfiledType pt) {
104    super();
105    this.collectionStatus = collectionStatus;
106    this.types.add(pt);
107  }
108  public String addType(String n) {
109    ProfiledType pt = new ProfiledType(n);
110    String res = pt.uri;
111    addType(pt);
112    return res;
113  }
114  public String addType(String n, String p) {
115    ProfiledType pt = new ProfiledType(n);
116    pt.addProfile(p);
117    String res = pt.uri;
118    addType(pt);
119    return res;
120  }
121  public void addType(ProfiledType pt) {
122    for (ProfiledType et : types) {
123      if (et.uri.equals(pt.uri)) {
124        if (pt.profiles != null) {
125          for (String p : pt.profiles) {
126            if (et.profiles == null)
127              et.profiles = new ArrayList<String>();
128            if (!et.profiles.contains(p))
129              et.profiles.add(p);
130          }
131        }
132        if (pt.bindings != null) {
133          for (ElementDefinitionBindingComponent b : pt.bindings) {
134            if (et.bindings == null)
135              et.bindings = new ArrayList<ElementDefinitionBindingComponent>();
136            if (!et.hasBinding(b))
137              et.bindings.add(b);
138          }
139        }
140        return;
141      }
142    }
143    types.add(pt); 
144  }
145  
146  public void addTypes(Collection<String> names) {
147    for (String n : names) 
148      addType(new ProfiledType(n));
149  }
150  
151  public boolean hasType(IWorkerContext context, String... tn) {
152    for (String n: tn) {
153      String t = ProfiledType.ns(n);
154      if (typesContains(t))
155        return true;
156      if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) {
157        t = FP_NS+Utilities.capitalize(n);
158        if (typesContains(t))
159          return true;
160      }
161    }
162    for (String n: tn) {
163      String id = n.contains("#") ? n.substring(0, n.indexOf("#")) : n;
164      String tail = null;
165      if (n.contains("#")) {
166        tail = n.substring( n.indexOf("#")+1);
167        tail = tail.substring(tail.indexOf("."));
168      }
169      String t = ProfiledType.ns(n);
170      StructureDefinition sd = context.fetchResource(StructureDefinition.class, t);
171      while (sd != null) {
172        if (tail == null && typesContains(sd.getUrl()))
173          return true;
174        if (tail == null && getSystemType(sd.getUrl()) != null && typesContains(getSystemType(sd.getUrl())))
175          return true;
176        if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
177          return true;
178        if (sd.hasBaseDefinition()) {
179          if (sd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Element") && !sd.getType().equals("string") && sd.getType().equals("uri"))
180            sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");
181          else
182            sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
183        } else
184          sd = null;
185      }
186    }
187    return false;
188  }
189  
190  private String getSystemType(String url) {
191    if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
192      String code = url.substring(40);
193      if (Utilities.existsInList(code, "string",  "boolean", "integer", "decimal", "dateTime", "time", "Quantity"))
194        return FP_NS+Utilities.capitalize(code);
195    }
196    return null;
197  }
198  
199  private boolean typesContains(String t) {
200    for (ProfiledType pt : types)
201      if (pt.uri.equals(t))
202        return true;
203    return false;
204  }
205  
206  public void update(TypeDetails source) {
207    for (ProfiledType pt : source.types)
208      addType(pt);
209    if (collectionStatus == null)
210      collectionStatus = source.collectionStatus;
211    else if (source.collectionStatus == CollectionStatus.UNORDERED)
212      collectionStatus = source.collectionStatus;
213    else
214      collectionStatus = CollectionStatus.ORDERED;
215  }
216  public TypeDetails union(TypeDetails right) {
217    TypeDetails result = new TypeDetails(null);
218    if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED)
219      result.collectionStatus = CollectionStatus.UNORDERED;
220    else 
221      result.collectionStatus = CollectionStatus.ORDERED;
222    for (ProfiledType pt : types)
223      result.addType(pt);
224    for (ProfiledType pt : right.types)
225      result.addType(pt);
226    return result;
227  }
228  
229  public TypeDetails intersect(TypeDetails right) {
230    TypeDetails result = new TypeDetails(null);
231    if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED)
232      result.collectionStatus = CollectionStatus.UNORDERED;
233    else 
234      result.collectionStatus = CollectionStatus.ORDERED;
235    for (ProfiledType pt : types) {
236      boolean found = false;
237      for (ProfiledType r : right.types)
238        found = found || pt.uri.equals(r.uri);
239      if (found)
240        result.addType(pt);
241    }
242    for (ProfiledType pt : right.types)
243      result.addType(pt);
244    return result;
245  }
246  
247  public boolean hasNoTypes() {
248    return types.isEmpty();
249  }
250  public Set<String> getTypes() {
251    Set<String> res = new HashSet<String>();
252    for (ProfiledType pt : types)
253      res.add(pt.uri);
254    return res;
255  }
256  public TypeDetails toSingleton() {
257    TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON);
258    result.types.addAll(types);
259    return result;
260  }
261  public CollectionStatus getCollectionStatus() {
262    return collectionStatus;
263  }
264  public boolean hasType(String n) {
265    String t = ProfiledType.ns(n);
266    if (typesContains(t))
267      return true;
268    if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) {
269      t = FP_NS+Utilities.capitalize(n);
270      if (typesContains(t))
271        return true;
272    }
273    return false;
274  }
275  
276  public boolean hasType(Set<String> tn) {
277    for (String n: tn) {
278      String t = ProfiledType.ns(n);
279      if (typesContains(t))
280        return true;
281      if (Utilities.existsInList(n, "boolean", "string", "integer", "decimal", "Quantity", "dateTime", "time", "ClassInfo", "SimpleTypeInfo")) {
282        t = FP_NS+Utilities.capitalize(n);
283        if (typesContains(t))
284          return true;
285      }
286    }
287    return false;
288  }
289  public String describe() {
290    return getTypes().toString();
291  }
292  public String getType() {
293    for (ProfiledType pt : types)
294      return pt.uri;
295    return null;
296  }
297  @Override
298  public String toString() {
299    return (collectionStatus == null ? collectionStatus.SINGLETON.toString() : collectionStatus.toString()) + getTypes().toString();
300  }
301  public String getTypeCode() throws DefinitionException {
302    if (types.size() != 1)
303      throw new DefinitionException("Multiple types? ("+types.toString()+")");
304    for (ProfiledType pt : types)
305      if (pt.uri.startsWith("http://hl7.org/fhir/StructureDefinition/"))
306        return pt.uri.substring(40);
307      else
308        return pt.uri;
309    return null;
310  }
311  public List<ProfiledType> getProfiledTypes() {
312    return types;
313  }
314  public boolean hasBinding() {
315    for (ProfiledType pt : types) {
316      if (pt.hasBindings())
317        return true;
318    }
319    return false;
320  }
321  public ElementDefinitionBindingComponent getBinding() {
322    for (ProfiledType pt : types) {
323      for (ElementDefinitionBindingComponent b : pt.getBindings())
324        return b;
325    }
326    return null;
327  }
328 
329  
330}