001package org.hl7.fhir.dstu2.utils;
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
032import java.io.ByteArrayInputStream;
033import java.io.FileNotFoundException;
034import java.io.IOException;
035import java.io.InputStream;
036import java.net.URISyntaxException;
037import java.util.ArrayList;
038import java.util.Collections;
039import java.util.HashMap;
040import java.util.List;
041import java.util.Map;
042import java.util.zip.ZipEntry;
043import java.util.zip.ZipInputStream;
044
045import org.hl7.fhir.dstu2.formats.IParser;
046import org.hl7.fhir.dstu2.formats.JsonParser;
047import org.hl7.fhir.dstu2.formats.ParserType;
048import org.hl7.fhir.dstu2.formats.XmlParser;
049import org.hl7.fhir.dstu2.model.Bundle;
050import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent;
051import org.hl7.fhir.dstu2.model.ConceptMap;
052import org.hl7.fhir.dstu2.model.ElementDefinition.ElementDefinitionBindingComponent;
053import org.hl7.fhir.dstu2.model.Resource;
054import org.hl7.fhir.dstu2.model.StructureDefinition;
055import org.hl7.fhir.dstu2.model.StructureDefinition.StructureDefinitionKind;
056import org.hl7.fhir.dstu2.model.ValueSet;
057import org.hl7.fhir.dstu2.terminologies.ValueSetExpansionCache;
058import org.hl7.fhir.dstu2.utils.ProfileUtilities.ProfileKnowledgeProvider;
059import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
060import org.hl7.fhir.dstu2.utils.validation.IResourceValidator;
061import org.hl7.fhir.exceptions.DefinitionException;
062import org.hl7.fhir.exceptions.FHIRException;
063import org.hl7.fhir.utilities.Utilities;
064import org.hl7.fhir.utilities.filesystem.CSFileInputStream;
065import org.hl7.fhir.utilities.validation.ValidationMessage;
066import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
067
068/*
069 * This is a stand alone implementation of worker context for use inside a tool.
070 * It loads from the validation package (validation-min.xml.zip), and has a 
071 * very light cient to connect to an open unauthenticated terminology service
072 */
073
074@Deprecated
075public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext, ProfileKnowledgeProvider {
076
077  // all maps are to the full URI
078  private Map<String, StructureDefinition> structures = new HashMap<String, StructureDefinition>();
079
080  // -- Initializations
081  /**
082   * Load the working context from the validation pack
083   * 
084   * @param path filename of the validation pack
085   * @return
086   * @throws IOException
087   * @throws FileNotFoundException
088   * @throws FHIRException
089   * @throws Exception
090   */
091  public static SimpleWorkerContext fromPack(String path) throws FileNotFoundException, IOException, FHIRException {
092    SimpleWorkerContext res = new SimpleWorkerContext();
093    res.loadFromPack(path);
094    return res;
095  }
096
097  public static SimpleWorkerContext fromClassPath() throws IOException, FHIRException {
098    SimpleWorkerContext res = new SimpleWorkerContext();
099    res.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.zip"));
100    return res;
101  }
102
103  public static SimpleWorkerContext fromDefinitions(Map<String, byte[]> source) throws IOException, FHIRException {
104    SimpleWorkerContext res = new SimpleWorkerContext();
105    for (String name : source.keySet()) {
106      if (name.endsWith(".xml")) {
107        res.loadFromFile(new ByteArrayInputStream(source.get(name)), name);
108      }
109    }
110    return res;
111  }
112
113  public void connectToTSServer(String url, String userAgent) throws URISyntaxException {
114    txServer = new FHIRToolingClient(url, userAgent);
115  }
116
117  private void loadFromFile(InputStream stream, String name) throws IOException, FHIRException {
118    XmlParser xml = new XmlParser();
119    Bundle f = (Bundle) xml.parse(stream);
120    for (BundleEntryComponent e : f.getEntry()) {
121
122      if (e.getFullUrl() == null) {
123        System.out.println("unidentified resource in " + name + " (no fullUrl)");
124      }
125      seeResource(e.getFullUrl(), e.getResource());
126    }
127  }
128
129  public void seeResource(String url, Resource r) throws FHIRException {
130    if (r instanceof StructureDefinition)
131      seeProfile(url, (StructureDefinition) r);
132    else if (r instanceof ValueSet)
133      seeValueSet(url, (ValueSet) r);
134    else if (r instanceof ConceptMap)
135      maps.put(((ConceptMap) r).getUrl(), (ConceptMap) r);
136  }
137
138  private void seeValueSet(String url, ValueSet vs) throws DefinitionException {
139    if (Utilities.noString(url))
140      url = vs.getUrl();
141    if (valueSets.containsKey(vs.getUrl()))
142      throw new DefinitionException("Duplicate Profile " + vs.getUrl());
143    valueSets.put(vs.getId(), vs);
144    valueSets.put(vs.getUrl(), vs);
145    if (!vs.getUrl().equals(url))
146      valueSets.put(url, vs);
147    if (vs.hasCodeSystem()) {
148      codeSystems.put(vs.getCodeSystem().getSystem().toString(), vs);
149    }
150  }
151
152  private void seeProfile(String url, StructureDefinition p) throws FHIRException {
153    if (Utilities.noString(url))
154      url = p.getUrl();
155    if (!p.hasSnapshot()) {
156      if (!p.hasBase())
157        throw new DefinitionException("Profile " + p.getName() + " (" + p.getUrl() + ") has no base and no snapshot");
158      StructureDefinition sd = fetchResource(StructureDefinition.class, p.getBase());
159      if (sd == null)
160        throw new DefinitionException(
161            "Profile " + p.getName() + " (" + p.getUrl() + ") base " + p.getBase() + " could not be resolved");
162      List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
163      ProfileUtilities pu = new ProfileUtilities(this, msgs, this);
164      pu.generateSnapshot(sd, p, p.getUrl(), p.getName());
165      for (ValidationMessage msg : msgs) {
166        if (msg.getLevel() == IssueSeverity.ERROR || msg.getLevel() == IssueSeverity.FATAL)
167          throw new DefinitionException(
168              "Profile " + p.getName() + " (" + p.getUrl() + "). Error generating snapshot: " + msg.getMessage());
169      }
170      if (!p.hasSnapshot())
171        throw new DefinitionException("Profile " + p.getName() + " (" + p.getUrl() + "). Error generating snapshot");
172      pu = null;
173    }
174    if (structures.containsKey(p.getUrl()))
175      throw new DefinitionException("Duplicate structures " + p.getUrl());
176    structures.put(p.getId(), p);
177    structures.put(p.getUrl(), p);
178    if (!p.getUrl().equals(url))
179      structures.put(url, p);
180  }
181
182  private void loadFromPack(String path) throws FileNotFoundException, IOException, FHIRException {
183    loadFromStream(new CSFileInputStream(path));
184  }
185
186  private void loadFromStream(InputStream stream) throws IOException, FHIRException {
187    ZipInputStream zip = new ZipInputStream(stream);
188    ZipEntry ze;
189    while ((ze = zip.getNextEntry()) != null) {
190      if (ze.getName().endsWith(".xml")) {
191        String name = ze.getName();
192        loadFromFile(zip, name);
193      }
194      zip.closeEntry();
195    }
196    zip.close();
197  }
198
199  @Override
200  public IParser getParser(ParserType type) {
201    switch (type) {
202    case JSON:
203      return newJsonParser();
204    case XML:
205      return newXmlParser();
206    default:
207      throw new Error("Parser Type " + type.toString() + " not supported");
208    }
209  }
210
211  @Override
212  public IParser getParser(String type) {
213    if (type.equalsIgnoreCase("JSON"))
214      return new JsonParser();
215    if (type.equalsIgnoreCase("XML"))
216      return new XmlParser();
217    throw new Error("Parser Type " + type.toString() + " not supported");
218  }
219
220  @Override
221  public IParser newJsonParser() {
222    return new JsonParser();
223  }
224
225  @Override
226  public IParser newXmlParser() {
227    return new XmlParser();
228  }
229
230  @Override
231  public <T extends Resource> boolean hasResource(Class<T> class_, String uri) {
232    try {
233      return fetchResource(class_, uri) != null;
234    } catch (Exception e) {
235      return false;
236    }
237  }
238
239  @Override
240  public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) {
241    return new NarrativeGenerator(prefix, basePath, this);
242  }
243
244  @Override
245  public IResourceValidator newValidator() {
246    throw new Error("not supported at this time");
247  }
248
249  @SuppressWarnings("unchecked")
250  @Override
251  public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
252    if (class_ == StructureDefinition.class && !uri.contains("/"))
253      uri = "http://hl7.org/fhir/StructureDefinition/" + uri;
254
255    if (uri.startsWith("http:")) {
256      if (uri.contains("#"))
257        uri = uri.substring(0, uri.indexOf("#"));
258      if (class_ == StructureDefinition.class) {
259        if (structures.containsKey(uri))
260          return (T) structures.get(uri);
261        else
262          return null;
263      } else if (class_ == ValueSet.class) {
264        if (valueSets.containsKey(uri))
265          return (T) valueSets.get(uri);
266        else if (codeSystems.containsKey(uri))
267          return (T) codeSystems.get(uri);
268        else
269          return null;
270      }
271    }
272    if (class_ == null && uri.contains("/")) {
273      return null;
274    }
275
276    throw new Error("not done yet");
277  }
278
279  public int totalCount() {
280    return valueSets.size() + maps.size() + structures.size();
281  }
282
283  public void setCache(ValueSetExpansionCache cache) {
284    this.expansionCache = cache;
285  }
286
287  @Override
288  public List<String> getResourceNames() {
289    List<String> result = new ArrayList<String>();
290    for (StructureDefinition sd : structures.values()) {
291      if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.hasConstrainedType())
292        result.add(sd.getName());
293    }
294    Collections.sort(result);
295    return result;
296  }
297
298  @Override
299  public String getAbbreviation(String name) {
300    return "xxx";
301  }
302
303  @Override
304  public boolean isDatatype(String typeSimple) {
305    // TODO Auto-generated method stub
306    return false;
307  }
308
309  @Override
310  public boolean isResource(String t) {
311    StructureDefinition sd;
312    try {
313      sd = fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + t);
314    } catch (Exception e) {
315      return false;
316    }
317    if (sd == null)
318      return false;
319    if (sd.hasConstrainedType())
320      return false;
321    return sd.getKind() == StructureDefinitionKind.RESOURCE;
322  }
323
324  @Override
325  public boolean hasLinkFor(String typeSimple) {
326    return false;
327  }
328
329  @Override
330  public String getLinkFor(String typeSimple) {
331    return null;
332  }
333
334  @Override
335  public BindingResolution resolveBinding(ElementDefinitionBindingComponent binding) {
336    return null;
337  }
338
339  @Override
340  public String getLinkForProfile(StructureDefinition profile, String url) {
341    return null;
342  }
343
344  @Override
345  public List<StructureDefinition> allStructures() {
346    List<StructureDefinition> res = new ArrayList<StructureDefinition>();
347    res.addAll(structures.values());
348    return res;
349  }
350
351}