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