001package org.hl7.fhir.r5.context;
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.File;
034import java.io.FileInputStream;
035import java.io.FileNotFoundException;
036import java.io.IOException;
037import java.io.InputStream;
038import java.util.ArrayList;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Locale;
042import java.util.Map;
043import java.util.Set;
044import java.util.UUID;
045import java.util.zip.ZipEntry;
046import java.util.zip.ZipInputStream;
047
048import lombok.AccessLevel;
049import lombok.AllArgsConstructor;
050import lombok.With;
051import org.apache.commons.io.IOUtils;
052import org.hl7.fhir.exceptions.DefinitionException;
053import org.hl7.fhir.exceptions.FHIRException;
054import org.hl7.fhir.exceptions.FHIRFormatError;
055import org.hl7.fhir.exceptions.TerminologyServiceException;
056import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
057import org.hl7.fhir.r5.context.ILoggingService.LogCategory;
058import org.hl7.fhir.r5.formats.IParser;
059import org.hl7.fhir.r5.formats.JsonParser;
060import org.hl7.fhir.r5.formats.XmlParser;
061import org.hl7.fhir.r5.model.*;
062import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
063import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
064import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
065import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
066import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
067import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
068import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
069import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext;
070import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
071import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
072import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
073import org.hl7.fhir.r5.utils.validation.IResourceValidator;
074import org.hl7.fhir.r5.utils.R5Hacker;
075import org.hl7.fhir.r5.utils.XVerExtensionManager;
076import org.hl7.fhir.utilities.ByteProvider;
077import org.hl7.fhir.utilities.MagicResources;
078import org.hl7.fhir.utilities.TextFile;
079import org.hl7.fhir.utilities.TimeTracker;
080import org.hl7.fhir.utilities.Utilities;
081import org.hl7.fhir.utilities.VersionUtilities;
082import org.hl7.fhir.utilities.filesystem.CSFileInputStream;
083import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
084import org.hl7.fhir.utilities.i18n.I18nConstants;
085import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
086import org.hl7.fhir.utilities.npm.NpmPackage;
087import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
088
089import ca.uhn.fhir.parser.DataFormatException;
090
091/*
092 * This is a stand alone implementation of worker context for use inside a tool.
093 * It loads from the validation package (validation-min.xml.zip), and has a 
094 * very light client to connect to an open unauthenticated terminology service
095 */
096
097public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext {
098
099  public static class PackageResourceLoader extends CanonicalResourceProxy {
100
101    private final String filename;
102    private final IContextResourceLoader loader;
103    private PackageInformation pi;
104
105    public PackageResourceLoader(PackageResourceInformation pri, IContextResourceLoader loader, PackageInformation pi) {
106      super(pri.getResourceType(), pri.getId(), loader == null ? pri.getUrl() :loader.patchUrl(pri.getUrl(), pri.getResourceType()), pri.getVersion(), pri.getSupplements(), pri.getDerivation(), pri.getContent());
107      this.filename = pri.getFilename();
108      this.loader = loader;
109      this.pi = pi;
110    }
111
112    @Override
113    public CanonicalResource loadResource() {
114      try {
115        FileInputStream f = ManagedFileAccess.inStream(filename);
116        try  {
117          if (loader != null) {
118            return setPi(R5Hacker.fixR5BrokenResource((CanonicalResource) loader.loadResource(f, true)));
119          } else {
120            return setPi(R5Hacker.fixR5BrokenResource((CanonicalResource) new JsonParser().parse(f)));
121          }
122        } finally {
123          f.close();
124        }
125      } catch (Exception e) {
126        throw new FHIRException("Error loading "+filename+": "+e.getMessage(), e);
127      }
128    }
129
130    private CanonicalResource setPi(CanonicalResource cr) {
131      cr.setSourcePackage(pi);
132      return cr;
133    }
134  }
135
136  public interface ILoadFilter {
137    boolean isOkToLoad(Resource resource);
138    boolean isOkToLoad(String resourceType);
139  }
140
141  public interface IValidatorFactory {
142    IResourceValidator makeValidator(IWorkerContext ctxt) throws FHIRException;
143    IResourceValidator makeValidator(IWorkerContext ctxts, XVerExtensionManager xverManager) throws FHIRException;
144  }
145
146        private Questionnaire questionnaire;
147  private String revision;
148  private String date;
149  private IValidatorFactory validatorFactory;
150  private boolean progress;
151  private final List<String> loadedPackages = new ArrayList<>();
152  private boolean canNoTS;
153  private XVerExtensionManager xverManager;
154  private boolean allowLazyLoading = true;
155
156  private SimpleWorkerContext() throws IOException, FHIRException {
157    super();
158  }
159
160  private SimpleWorkerContext(Locale locale) throws IOException, FHIRException {
161    super(locale);
162  }
163
164  public SimpleWorkerContext(SimpleWorkerContext other) throws IOException, FHIRException {
165    super();
166    copy(other);
167  }
168
169  private SimpleWorkerContext(SimpleWorkerContext other, Locale locale) throws IOException, FHIRException {
170    super(locale);
171    copy(other);
172  }
173  
174  protected void copy(SimpleWorkerContext other) {
175    super.copy(other);
176    binaries.putAll(other.binaries);
177    version = other.version;
178    revision = other.revision;
179    date = other.date;
180    validatorFactory = other.validatorFactory;
181    progress = other.progress;
182    loadedPackages.addAll(other.loadedPackages);
183    canNoTS = other.canNoTS;
184    xverManager = other.xverManager;
185    allowLazyLoading = other.allowLazyLoading;
186  }
187
188
189  public List<String> getLoadedPackages() {
190    return loadedPackages;
191  }
192
193  // -- Initializations
194  @AllArgsConstructor(access = AccessLevel.PRIVATE)
195  public static class SimpleWorkerContextBuilder {
196
197
198    @With
199    private final String terminologyCachePath;
200    @With
201    private final boolean cacheTerminologyClientErrors;
202    @With
203    private final boolean alwaysUseTerminologyServer;
204    @With
205    private final boolean readOnlyCache;
206
207    @With
208    private final Locale locale;
209
210    @With
211    private final String userAgent;
212
213    @With
214    private final boolean allowLoadingDuplicates;
215
216    @With
217    private final org.hl7.fhir.r5.context.ILoggingService loggingService;
218
219    public SimpleWorkerContextBuilder() {
220      cacheTerminologyClientErrors = false;
221      alwaysUseTerminologyServer = false;
222      readOnlyCache = false;
223      terminologyCachePath = null;
224      locale = null;
225      userAgent = null;
226      allowLoadingDuplicates = false;
227      loggingService = new SystemOutLoggingService();
228    }
229
230    private SimpleWorkerContext getSimpleWorkerContextInstance() throws IOException {
231      if (locale != null) {
232        return new SimpleWorkerContext(locale);
233      } else {
234        return new SimpleWorkerContext();
235      }
236    }
237
238    public SimpleWorkerContext build() throws IOException {
239      SimpleWorkerContext context = getSimpleWorkerContextInstance();
240      return build(context);
241    }
242
243    private SimpleWorkerContext build(SimpleWorkerContext context) throws IOException {
244      context.initTxCache(terminologyCachePath);
245      context.setUserAgent(userAgent);
246      context.setLogger(loggingService);
247      context.cacheResource(new org.hl7.fhir.r5.formats.JsonParser().parse(MagicResources.spdxCodesAsData()));
248      return context;
249    }
250
251    public SimpleWorkerContext fromPackage(NpmPackage pi) throws IOException, FHIRException {
252      SimpleWorkerContext context = getSimpleWorkerContextInstance();
253      context.setAllowLoadingDuplicates(allowLoadingDuplicates);
254      context.terminologyClientManager.setFactory(TerminologyClientR5.factory());
255      context.loadFromPackage(pi, null);
256      return build(context);
257    }
258
259    public SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader, boolean genSnapshots) throws IOException, FHIRException {
260      SimpleWorkerContext context = getSimpleWorkerContextInstance();
261      context.setAllowLoadingDuplicates(allowLoadingDuplicates);      
262      context.version = pi.getNpm().asString("version");
263      context.terminologyClientManager.setFactory(loader.txFactory());
264      context.loadFromPackage(pi, loader);
265      context.finishLoading(genSnapshots);
266      return build(context);
267    }
268
269    /**
270     * Load the working context from the validation pack
271     *
272     * @param path
273     *           filename of the validation pack
274     * @return
275     * @throws IOException
276     * @throws FileNotFoundException
277     * @throws FHIRException
278     * @throws Exception
279     */
280    public  SimpleWorkerContext fromPack(String path) throws IOException, FHIRException {
281      SimpleWorkerContext context = getSimpleWorkerContextInstance();
282      context.setAllowLoadingDuplicates(allowLoadingDuplicates);
283      context.loadFromPack(path, null);
284      return build(context);
285    }
286
287    public SimpleWorkerContext fromPack(String path, IContextResourceLoader loader) throws IOException, FHIRException {
288      SimpleWorkerContext context = getSimpleWorkerContextInstance();
289      context.loadFromPack(path, loader);
290      return build(context);
291    }
292
293    public SimpleWorkerContext fromClassPath() throws IOException, FHIRException {
294      SimpleWorkerContext context = getSimpleWorkerContextInstance();
295      context.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.json.zip"), null);
296      return build(context);
297    }
298
299    public SimpleWorkerContext fromClassPath(String name) throws IOException, FHIRException {
300      SimpleWorkerContext context = getSimpleWorkerContextInstance();
301      InputStream s = SimpleWorkerContext.class.getResourceAsStream("/" + name);
302      context.setAllowLoadingDuplicates(allowLoadingDuplicates);
303      context.loadFromStream(s, null);
304      return build(context);
305    }
306
307    public SimpleWorkerContext fromDefinitions(Map<String, ByteProvider> source, IContextResourceLoader loader, PackageInformation pi) throws IOException, FHIRException  {
308      SimpleWorkerContext context = getSimpleWorkerContextInstance();
309      for (String name : source.keySet()) {
310        try {
311          context.loadDefinitionItem(name, new ByteArrayInputStream(source.get(name).getBytes()), loader, null, pi);
312        } catch (Exception e) {
313          System.out.println("Error loading "+name+": "+e.getMessage());
314          throw new FHIRException("Error loading "+name+": "+e.getMessage(), e);
315        }
316      }
317      return build(context);
318    }
319    public SimpleWorkerContext fromNothing() throws FHIRException, IOException  {
320      return build();
321    }
322  }
323
324  private void loadDefinitionItem(String name, InputStream stream, IContextResourceLoader loader, ILoadFilter filter, PackageInformation pi) throws IOException, FHIRException {
325    if (name.endsWith(".xml"))
326      loadFromFile(stream, name, loader, filter);
327    else if (name.endsWith(".json"))
328      loadFromFileJson(stream, name, loader, filter, pi);
329    else if (name.equals("version.info"))
330      readVersionInfo(stream);
331    else
332      loadBytes(name, stream);
333  }
334
335  public void connectToTSServer(ITerminologyClientFactory factory, ITerminologyClient client, boolean useEcosystem) {
336    terminologyClientManager.setFactory(factory);
337    if (txLog == null) {
338      txLog = client.getLogger();
339    }
340    TerminologyClientContext tcc = terminologyClientManager.setMasterClient(client, useEcosystem);
341    txLog("Connect to "+client.getAddress());
342    try {
343      tcc.initialize();  
344    } catch (Exception e) {
345      if (canRunWithoutTerminology) {
346        noTerminologyServer = true;
347        logger.logMessage("==============!! Running without terminology server !! ==============");
348        if (terminologyClientManager.getMasterClient() != null) {
349          logger.logMessage("txServer = "+ terminologyClientManager.getMasterClient().getId());
350          logger.logMessage("Error = "+e.getMessage()+"");
351        }
352        logger.logMessage("=====================================================================");
353      } else {
354        e.printStackTrace();
355        throw new TerminologyServiceException(e);
356      }
357    }      
358  }
359  
360  public void connectToTSServer(ITerminologyClientFactory factory, String address, String software, String log, boolean useEcosystem) {
361    try {
362      terminologyClientManager.setFactory(factory);
363      if (log != null && (log.endsWith(".htm") || log.endsWith(".html"))) {
364        txLog = new HTMLClientLogger(log);
365      } else {
366        txLog = new TextClientLogger(log);
367      }      
368      ITerminologyClient client = factory.makeClient("tx-server", address, software, txLog);
369      // txFactory.makeClient("Tx-Server", txServer, "fhir/publisher", null)
370//      terminologyClientManager.setLogger(txLog);
371//      terminologyClientManager.setUserAgent(userAgent);
372      connectToTSServer(factory, client, useEcosystem);
373      
374    } catch (Exception e) {
375      e.printStackTrace();
376      throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), address), e);
377    }
378  }
379
380  public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader) throws FHIRException {
381    loadFromFile(stream, name, loader, null);
382  }
383  
384        public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader, ILoadFilter filter) throws FHIRException {
385                Resource f;
386                try {
387                  if (loader != null)
388                    f = loader.loadBundle(stream, false);
389                  else {
390                    XmlParser xml = new XmlParser();
391                    f = xml.parse(stream);
392                  }
393    } catch (DataFormatException e1) {
394      throw new org.hl7.fhir.exceptions.FHIRFormatError(formatMessage(I18nConstants.ERROR_PARSING_, name, e1.getMessage()), e1);
395    } catch (Exception e1) {
396                        throw new org.hl7.fhir.exceptions.FHIRFormatError(formatMessage(I18nConstants.ERROR_PARSING_, name, e1.getMessage()), e1);
397                }
398                if (f instanceof Bundle) {
399                  Bundle bnd = (Bundle) f;
400                  for (BundleEntryComponent e : bnd.getEntry()) {
401                    if (e.getFullUrl() == null) {
402                      logger.logDebugMessage(LogCategory.CONTEXT, "unidentified resource in " + name+" (no fullUrl)");
403                    }
404              if (filter == null || filter.isOkToLoad(e.getResource())) {
405                String path = loader != null ? loader.getResourcePath(e.getResource()) : null;
406                if (path != null) {
407                  e.getResource().setWebPath(path);
408                }
409                      cacheResource(e.getResource());
410              }
411                  }
412                } else if (f instanceof CanonicalResource) {
413                  if (filter == null || filter.isOkToLoad(f)) {
414        String path = loader != null ? loader.getResourcePath(f) : null;
415        if (path != null) {
416          f.setWebPath(path);
417        }
418                    cacheResource(f);
419                  }
420                }
421        }
422
423  private void loadFromFileJson(InputStream stream, String name, IContextResourceLoader loader, ILoadFilter filter, PackageInformation pi) throws IOException, FHIRException {
424    Bundle f = null;
425    try {
426      if (loader != null)
427        f = loader.loadBundle(stream, true);
428      else {
429        JsonParser json = new JsonParser();
430        Resource r = json.parse(stream);
431        if (r instanceof Bundle)
432          f = (Bundle) r;
433        else if (filter == null || filter.isOkToLoad(f)) {
434          cacheResourceFromPackage(r, pi);
435        }
436      }
437    } catch (FHIRFormatError e1) {
438      throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1);
439    }
440    if (f != null)
441      for (BundleEntryComponent e : f.getEntry()) {
442        if (filter == null || filter.isOkToLoad(e.getResource())) {
443          String path = loader != null ? loader.getResourcePath(e.getResource()) : null;
444          if (path != null) {
445            e.getResource().setWebPath(path);
446          }
447          cacheResourceFromPackage(e.getResource(), pi);
448        }
449    }
450  }
451
452        private void loadFromPack(String path, IContextResourceLoader loader) throws IOException, FHIRException {
453                loadFromStream(new CSFileInputStream(path), loader);
454        }
455  
456
457  @Override
458  public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws IOException, FHIRException {
459    return loadFromPackageInt(pi, loader, loader == null ? defaultTypesToLoad() : loader.getTypes());
460  }
461  
462  public static List<String> defaultTypesToLoad() {
463    // there's no penalty for listing resources that don't exist, so we just all the relevant possibilities for all versions 
464    return Utilities.strings("CodeSystem", "ValueSet", "ConceptMap", "NamingSystem",
465                         "StructureDefinition", "StructureMap", 
466                         "SearchParameter", "OperationDefinition", "CapabilityStatement", "Conformance",
467                         "Questionnaire", "ImplementationGuide", "Measure" );
468  }
469
470  @Override
471  public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, List<String> types) throws IOException, FHIRException {
472    return loadFromPackageInt(pi, loader, types);
473  }
474 
475  @Override
476  public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws IOException, FHIRException {
477    return loadFromPackageAndDependenciesInt(pi, loader, pcm, pi.name()+"#"+pi.version());
478  }
479  public int loadFromPackageAndDependenciesInt(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm, String path) throws IOException, FHIRException {
480    int t = 0;
481
482    for (String e : pi.dependencies()) {
483      if (!loadedPackages.contains(e) && !VersionUtilities.isCorePackage(e)) {
484        NpmPackage npm = pcm.loadPackage(e);
485        if (!VersionUtilities.versionsMatch(version, npm.fhirVersion())) {
486          System.out.println(formatMessage(I18nConstants.PACKAGE_VERSION_MISMATCH, e, version, npm.fhirVersion(), path));  
487        }
488        t = t + loadFromPackageAndDependenciesInt(npm, loader.getNewLoader(npm), pcm, path+" -> "+npm.name()+"#"+npm.version());
489      }
490    }
491    t = t + loadFromPackageInt(pi, loader, loader.getTypes());
492    return t;
493  }
494
495
496  public int loadFromPackageInt(NpmPackage pi, IContextResourceLoader loader, List<String> types) throws IOException, FHIRException {
497    int t = 0;
498    if (progress) {
499      System.out.println("Load Package "+pi.name()+"#"+pi.version());
500    }
501    if (loadedPackages.contains(pi.id()+"#"+pi.version())) {
502      return 0;
503    }
504    
505    loadedPackages.add(pi.id()+"#"+pi.version());
506    if (packageTracker != null) {
507      packageTracker.packageLoaded(pi.id(), pi.version());
508    }
509    
510    String of = pi.getFolders().get("package").getFolderPath();
511    if (of != null) {
512      oidSources.add(new OIDSource(of, pi.vid()));
513    }
514    
515    if ((types == null || types.size() == 0) &&  loader != null) {
516      types = loader.getTypes();
517    }
518    PackageInformation pii = new PackageInformation(pi);
519    if (VersionUtilities.isR2Ver(pi.fhirVersion()) || !pi.canLazyLoad() || !allowLazyLoading) {
520      // can't lazy load R2 because of valueset/codesystem implementation
521      if (types == null || types.size() == 0) {
522        types = Utilities.strings("StructureDefinition", "ValueSet", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem" );
523      }
524      for (String s : pi.listResources(types)) {
525        try {
526          loadDefinitionItem(s, pi.load("package", s), loader, null, pii);
527          t++;
528        } catch (Exception e) {
529          throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, s, pi.name(), pi.version(), e.getMessage()), e);
530        }      
531      }
532    } else {
533      if (types == null || types.size() == 0) {
534        types = Utilities.strings("StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem", "Measures" );
535      }
536      for (PackageResourceInformation pri : pi.listIndexedResources(types)) {
537        if (!pri.getFilename().contains("ig-r4") && (loader == null || loader.wantLoad(pi, pri))) {
538          try {
539            if (!pri.hasId()) {
540              loadDefinitionItem(pri.getFilename(), ManagedFileAccess.inStream(pri.getFilename()), loader, null, pii);
541            } else {
542              registerResourceFromPackage(new PackageResourceLoader(pri, loader, pii), pii);
543            }
544            t++;
545          } catch (FHIRException e) {
546            throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, pri.getFilename(), pi.name(), pi.version(), e.getMessage()), e);
547          }
548        }
549      }
550    }
551          for (String s : pi.list("other")) {
552            binaries.put(s, TextFile.streamToBytes(pi.load("other", s)));
553          }
554          if (version == null) {
555            version = pi.version();
556            if (version.equals("current")) {
557              version = "5.0.0";
558            }
559          }
560          if (loader != null && terminologyClientManager.getFactory() == null) {
561            terminologyClientManager.setFactory(loader.txFactory());
562          }
563          return t;
564        }
565
566  public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException {
567    loadDefinitionItem(file, new CSFileInputStream(file), loader, null, null);
568  }
569  
570        private void loadFromStream(InputStream stream, IContextResourceLoader loader) throws IOException, FHIRException {
571                ZipInputStream zip = new ZipInputStream(stream);
572    ZipEntry zipEntry;
573    while ((zipEntry = zip.getNextEntry()) != null) {
574      String entryName = zipEntry.getName();
575      if (entryName.contains("..")) {
576        throw new RuntimeException("Entry with an illegal path: " + entryName);
577      }
578      loadDefinitionItem(entryName, zip, loader, null, null);
579                        zip.closeEntry();
580                }
581                zip.close();
582        }
583
584  private void readVersionInfo(InputStream stream) throws IOException, DefinitionException {
585    byte[] bytes = IOUtils.toByteArray(stream);
586    binaries.put("version.info", bytes);
587
588    String[] vi = new String(bytes).split("\\r?\\n");
589    for (String s : vi) {
590      if (s.startsWith("version=")) {
591        if (version == null)
592        version = s.substring(8);
593        else if (!version.equals(s.substring(8))) {
594          throw new DefinitionException(formatMessage(I18nConstants.VERSION_MISMATCH_THE_CONTEXT_HAS_VERSION__LOADED_AND_THE_NEW_CONTENT_BEING_LOADED_IS_VERSION_, version, s.substring(8)));
595        }
596      }
597      if (s.startsWith("revision="))
598        revision = s.substring(9);
599      if (s.startsWith("date="))
600        date = s.substring(5);
601    }
602  }
603
604        private void loadBytes(String name, InputStream stream) throws IOException {
605    byte[] bytes = IOUtils.toByteArray(stream);
606          binaries.put(name, bytes);
607  }
608
609        @Override
610        public IResourceValidator newValidator() throws FHIRException {
611          if (validatorFactory == null)
612            throw new Error(formatMessage(I18nConstants.NO_VALIDATOR_CONFIGURED));
613          return validatorFactory.makeValidator(this, xverManager).setJurisdiction(JurisdictionUtilities.getJurisdictionCodingFromLocale(Locale.getDefault().getCountry()));
614        }
615
616
617
618
619  @Override
620  public List<String> getResourceNames() {
621    Set<String> result = new HashSet<String>();
622    for (StructureDefinition sd : listStructures()) {
623      if (sd.getKind() == StructureDefinitionKind.RESOURCE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.hasUserData("old.load.mode"))
624        result.add(sd.getName());
625    }
626    return Utilities.sorted(result);
627  }
628
629 
630  public Questionnaire getQuestionnaire() {
631    return questionnaire;
632  }
633
634  public void setQuestionnaire(Questionnaire questionnaire) {
635    this.questionnaire = questionnaire;
636  }
637
638 
639
640  public void loadBinariesFromFolder(String folder) throws IOException {
641    for (String n : ManagedFileAccess.file(folder).list()) {
642      loadBytes(n, ManagedFileAccess.inStream(Utilities.path(folder, n)));
643    }
644  }
645  
646  public void loadBinariesFromFolder(NpmPackage pi) throws IOException {
647    for (String n : pi.list("other")) {
648      loadBytes(n, pi.load("other", n));
649    }
650  }
651  
652  public void loadFromFolder(String folder) throws IOException {
653    for (String n : ManagedFileAccess.file(folder).list()) {
654      if (n.endsWith(".json")) 
655        loadFromFile(Utilities.path(folder, n), new JsonParser());
656      else if (n.endsWith(".xml")) 
657        loadFromFile(Utilities.path(folder, n), new XmlParser());
658    }
659  }
660  
661  private void loadFromFile(String filename, IParser p) {
662        Resource r; 
663        try {
664                r = p.parse(ManagedFileAccess.inStream(filename));
665      if (r.getResourceType() == ResourceType.Bundle) {
666        for (BundleEntryComponent e : ((Bundle) r).getEntry()) {
667          cacheResource(e.getResource());
668        }
669     } else {
670       cacheResource(r);
671     }
672        } catch (Exception e) {
673        return;
674    }
675  }
676
677 
678
679  @Override
680  public String getVersion() {
681    return version;
682  }
683
684  
685  public List<StructureMap> findTransformsforSource(String url) {
686    List<StructureMap> res = new ArrayList<StructureMap>();
687    for (StructureMap map : fetchResourcesByType(StructureMap.class)) {
688      boolean match = false;
689      boolean ok = true;
690      for (StructureMapStructureComponent t : map.getStructure()) {
691        if (t.getMode() == StructureMapModelMode.SOURCE) {
692          match = match || t.getUrl().equals(url);
693          ok = ok && t.getUrl().equals(url);
694        }
695      }
696      if (match && ok)
697        res.add(map);
698    }
699    return res;
700  }
701
702  public IValidatorFactory getValidatorFactory() {
703    return validatorFactory;
704  }
705
706  public void setValidatorFactory(IValidatorFactory validatorFactory) {
707    this.validatorFactory = validatorFactory;
708  }
709
710  @Override
711  public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
712    T r = super.fetchResource(class_, uri);
713    if (r instanceof StructureDefinition) {
714      StructureDefinition p = (StructureDefinition)r;
715      try {
716        new ContextUtilities(this).generateSnapshot(p);
717      } catch (Exception e) {
718        // not sure what to do in this case?
719        System.out.println("Unable to generate snapshot @3 for "+uri+": "+e.getMessage());
720        if (logger.isDebugLogging()) {
721          e.printStackTrace();          
722        }
723      }
724    }
725    return r;
726  }
727
728  @Override
729  public <T extends Resource> T fetchResourceRaw(Class<T> class_, String uri) {
730    T r = super.fetchResource(class_, uri);
731    return r;
732  }
733
734  @Override
735  public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource source) {
736    T r = super.fetchResource(class_, uri, source);
737    if (r instanceof StructureDefinition) {
738      StructureDefinition p = (StructureDefinition)r;
739      if (!p.isGeneratedSnapshot()) {
740        if (p.isGeneratingSnapshot()) {
741          throw new FHIRException("Attempt to fetch the profile "+p.getVersionedUrl()+" while generating the snapshot for it");
742        }
743        try {
744          if (logger.isDebugLogging()) {
745            System.out.println("Generating snapshot for "+p.getVersionedUrl());
746          }
747          p.setGeneratingSnapshot(true);
748          try {
749            new ContextUtilities(this).generateSnapshot(p);
750          } finally {
751            p.setGeneratingSnapshot(false);      
752          }
753        } catch (Exception e) {
754          // not sure what to do in this case?
755          System.out.println("Unable to generate snapshot @4 for "+p.getVersionedUrl()+": "+e.getMessage());
756          if (logger.isDebugLogging()) {
757            e.printStackTrace();
758          }
759        }
760      }
761    }
762    return r;
763  }
764
765
766
767
768  public String listMapUrls() {
769    return Utilities.listCanonicalUrls(transforms.keys());
770  }
771
772  public boolean isProgress() {
773    return progress;
774  }
775
776  public void setProgress(boolean progress) {
777    this.progress = progress;
778  }
779
780  public void setClock(TimeTracker tt) {
781    clock = tt;
782  }
783
784  public boolean isCanNoTS() {
785    return canNoTS;
786  }
787
788  public void setCanNoTS(boolean canNoTS) {
789    this.canNoTS = canNoTS;
790  }
791
792  public XVerExtensionManager getXVer() {
793    if (xverManager == null) {
794      xverManager = new XVerExtensionManager(this);
795    }
796   return xverManager;
797  }
798  
799  public void cachePackage(PackageInformation packageInfo) {
800    // nothing yet
801  }
802
803  @Override
804  public boolean hasPackage(String id, String ver) {
805    return loadedPackages.contains(id+"#"+ver);
806  }
807
808  public boolean hasPackage(String idAndver) {
809    if (loadedPackages.contains(idAndver)) {
810      return true;
811    }
812    // not clear whether the same logic should apply to other cross-version packages?
813    if (idAndver.startsWith("hl7.fhir.uv.extensions")) {
814      String v = idAndver.substring(idAndver.lastIndexOf("#")+1);
815      for (String s : loadedPackages) {
816        String v2 = s.substring(s.lastIndexOf("#")+1);
817        if (s.startsWith("hl7.fhir.uv.extensions.") && VersionUtilities.versionsMatch(v, v2)) {
818          return true;
819        }
820      }
821    }
822    return false;
823    
824  }
825
826  @Override
827  public boolean hasPackage(PackageInformation pack) {
828    return false;
829  }
830
831  @Override
832  public PackageInformation getPackage(String id, String ver) {
833    return null;
834  }
835
836  public boolean isAllowLazyLoading() {
837    return allowLazyLoading;
838  }
839
840  public void setAllowLazyLoading(boolean allowLazyLoading) {
841    this.allowLazyLoading = allowLazyLoading;
842  }
843
844  public String loadedPackageSummary() {
845     return loadedPackages.toString();
846  }
847
848  @Override
849  public String getSpecUrl() {
850    return VersionUtilities.getSpecUrl(getVersion())+"/";
851  }
852
853
854
855}
856