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