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