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