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