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