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