001package org.hl7.fhir.r4.context;
002
003import java.io.FileNotFoundException;
004import java.io.IOException;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Date;
008import java.util.HashMap;
009import java.util.HashSet;
010import java.util.List;
011import java.util.Locale;
012import java.util.Map;
013import java.util.ResourceBundle;
014import java.util.Set;
015
016import lombok.extern.slf4j.Slf4j;
017import org.apache.commons.lang3.StringUtils;
018import org.fhir.ucum.UcumService;
019import org.hl7.fhir.exceptions.DefinitionException;
020import org.hl7.fhir.exceptions.FHIRException;
021import org.hl7.fhir.exceptions.TerminologyServiceException;
022import org.hl7.fhir.r4.conformance.ProfileUtilities;
023import org.hl7.fhir.r4.context.TerminologyCache.CacheToken;
024import org.hl7.fhir.r4.model.BooleanType;
025import org.hl7.fhir.r4.model.CapabilityStatement;
026import org.hl7.fhir.r4.model.CodeSystem;
027import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
028import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
029import org.hl7.fhir.r4.model.CodeableConcept;
030import org.hl7.fhir.r4.model.Coding;
031import org.hl7.fhir.r4.model.ConceptMap;
032import org.hl7.fhir.r4.model.Constants;
033import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
034import org.hl7.fhir.r4.model.ImplementationGuide;
035import org.hl7.fhir.r4.model.IntegerType;
036import org.hl7.fhir.r4.model.MetadataResource;
037import org.hl7.fhir.r4.model.NamingSystem;
038import org.hl7.fhir.r4.model.NamingSystem.NamingSystemIdentifierType;
039import org.hl7.fhir.r4.model.NamingSystem.NamingSystemUniqueIdComponent;
040import org.hl7.fhir.r4.model.OperationDefinition;
041import org.hl7.fhir.r4.model.Parameters;
042import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
043import org.hl7.fhir.r4.model.PlanDefinition;
044import org.hl7.fhir.r4.model.Questionnaire;
045import org.hl7.fhir.r4.model.Reference;
046import org.hl7.fhir.r4.model.Resource;
047import org.hl7.fhir.r4.model.SearchParameter;
048import org.hl7.fhir.r4.model.StringType;
049import org.hl7.fhir.r4.model.StructureDefinition;
050import org.hl7.fhir.r4.model.StructureMap;
051import org.hl7.fhir.r4.model.TerminologyCapabilities;
052import org.hl7.fhir.r4.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
053import org.hl7.fhir.r4.model.ValueSet;
054import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
055import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent;
056import org.hl7.fhir.r4.terminologies.TerminologyClient;
057import org.hl7.fhir.r4.terminologies.ValueSetCheckerSimple;
058import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
059import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
060import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple;
061import org.hl7.fhir.r4.utils.ToolingExtensions;
062import org.hl7.fhir.r4.model.DomainResource;
063import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
064import org.hl7.fhir.utilities.OIDUtilities;
065import org.hl7.fhir.utilities.TranslationServices;
066import org.hl7.fhir.utilities.UUIDUtilities;
067import org.hl7.fhir.utilities.Utilities;
068import org.hl7.fhir.utilities.i18n.I18nBase;
069import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
070import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
071import org.hl7.fhir.utilities.validation.ValidationOptions;
072
073/*
074  Copyright (c) 2011+, HL7, Inc.
075  All rights reserved.
076  
077  Redistribution and use in source and binary forms, with or without modification, 
078  are permitted provided that the following conditions are met:
079    
080   * Redistributions of source code must retain the above copyright notice, this 
081     list of conditions and the following disclaimer.
082   * Redistributions in binary form must reproduce the above copyright notice, 
083     this list of conditions and the following disclaimer in the documentation 
084     and/or other materials provided with the distribution.
085   * Neither the name of HL7 nor the names of its contributors may be used to 
086     endorse or promote products derived from this software without specific 
087     prior written permission.
088  
089  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
090  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
091  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
092  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
093  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
094  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
095  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
096  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
097  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
098  POSSIBILITY OF SUCH DAMAGE.
099  
100 */
101
102import com.google.gson.JsonObject;
103
104@MarkedToMoveToAdjunctPackage
105@Slf4j
106public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext {
107
108
109  private Object lock = new Object(); // used as a lock for the data that follows
110
111  private Map<String, Map<String, Resource>> allResourcesById = new HashMap<String, Map<String, Resource>>();
112  // all maps are to the full URI
113  private Map<String, CodeSystem> codeSystems = new HashMap<String, CodeSystem>();
114  private Set<String> supportedCodeSystems = new HashSet<String>();
115  private Map<String, ValueSet> valueSets = new HashMap<String, ValueSet>();
116  private Map<String, ConceptMap> maps = new HashMap<String, ConceptMap>();
117  protected Map<String, StructureMap> transforms = new HashMap<String, StructureMap>();
118  private Map<String, StructureDefinition> structures = new HashMap<String, StructureDefinition>();
119  private Map<String, ImplementationGuide> guides = new HashMap<String, ImplementationGuide>();
120  private Map<String, CapabilityStatement> capstmts = new HashMap<String, CapabilityStatement>();
121  private Map<String, SearchParameter> searchParameters = new HashMap<String, SearchParameter>();
122  private Map<String, Questionnaire> questionnaires = new HashMap<String, Questionnaire>();
123  private Map<String, OperationDefinition> operations = new HashMap<String, OperationDefinition>();
124  private Map<String, PlanDefinition> plans = new HashMap<String, PlanDefinition>();
125  private List<NamingSystem> systems = new ArrayList<NamingSystem>();
126  private UcumService ucumService;
127
128  protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String, ValidationResult>>();
129  protected String tsServer;
130  protected String name;
131  private boolean allowLoadingDuplicates;
132
133  protected TerminologyClient txClient;
134  protected HTMLClientLogger txLog;
135  private TerminologyCapabilities txcaps;
136  private boolean canRunWithoutTerminology;
137  protected boolean noTerminologyServer;
138  private int expandCodesLimit = 1000;
139
140  protected Parameters expParameters;
141  private TranslationServices translator = new NullTranslator();
142  protected TerminologyCache txCache;
143
144  private boolean tlogging = true;
145  private Locale locale;
146  private ResourceBundle i18Nmessages;
147
148  public BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException {
149    super();
150    txCache = new TerminologyCache(lock, null);
151  }
152
153  public BaseWorkerContext(Map<String, CodeSystem> codeSystems, Map<String, ValueSet> valueSets,
154      Map<String, ConceptMap> maps, Map<String, StructureDefinition> profiles, Map<String, ImplementationGuide> guides)
155      throws FileNotFoundException, IOException, FHIRException {
156    super();
157    this.codeSystems = codeSystems;
158    this.valueSets = valueSets;
159    this.maps = maps;
160    this.structures = profiles;
161    this.guides = guides;
162    txCache = new TerminologyCache(lock, null);
163  }
164
165  protected void copy(BaseWorkerContext other) {
166    synchronized (other.lock) { // tricky, because you need to lock this as well, but it's really not in use yet
167      allResourcesById.putAll(other.allResourcesById);
168      translator = other.translator;
169      codeSystems.putAll(other.codeSystems);
170      txcaps = other.txcaps;
171      valueSets.putAll(other.valueSets);
172      maps.putAll(other.maps);
173      transforms.putAll(other.transforms);
174      structures.putAll(other.structures);
175      searchParameters.putAll(other.searchParameters);
176      plans.putAll(other.plans);
177      questionnaires.putAll(other.questionnaires);
178      operations.putAll(other.operations);
179      systems.addAll(other.systems);
180      guides.putAll(other.guides);
181      capstmts.putAll(other.capstmts);
182
183      allowLoadingDuplicates = other.allowLoadingDuplicates;
184      tsServer = other.tsServer;
185      name = other.name;
186      txClient = other.txClient;
187      txLog = other.txLog;
188      txcaps = other.txcaps;
189      canRunWithoutTerminology = other.canRunWithoutTerminology;
190      noTerminologyServer = other.noTerminologyServer;
191      if (other.txCache != null)
192        txCache = other.txCache.copy();
193      expandCodesLimit = other.expandCodesLimit;
194
195      expParameters = other.expParameters;
196    }
197  }
198
199  public void cacheResource(Resource r) throws FHIRException {
200    synchronized (lock) {
201      Map<String, Resource> map = allResourcesById.get(r.fhirType());
202      if (map == null) {
203        map = new HashMap<String, Resource>();
204        allResourcesById.put(r.fhirType(), map);
205      }
206      map.put(r.getId(), r);
207
208      if (r instanceof MetadataResource) {
209        MetadataResource m = (MetadataResource) r;
210        String url = m.getUrl();
211        if (!allowLoadingDuplicates && hasResource(r.getClass(), url))
212          throw new DefinitionException("Duplicate Resource " + url);
213        if (r instanceof StructureDefinition)
214          seeMetadataResource((StructureDefinition) m, structures, false);
215        else if (r instanceof ValueSet)
216          seeMetadataResource((ValueSet) m, valueSets, false);
217        else if (r instanceof CodeSystem)
218          seeMetadataResource((CodeSystem) m, codeSystems, false);
219        else if (r instanceof ImplementationGuide)
220          seeMetadataResource((ImplementationGuide) m, guides, false);
221        else if (r instanceof CapabilityStatement)
222          seeMetadataResource((CapabilityStatement) m, capstmts, false);
223        else if (r instanceof SearchParameter)
224          seeMetadataResource((SearchParameter) m, searchParameters, false);
225        else if (r instanceof PlanDefinition)
226          seeMetadataResource((PlanDefinition) m, plans, false);
227        else if (r instanceof OperationDefinition)
228          seeMetadataResource((OperationDefinition) m, operations, false);
229        else if (r instanceof Questionnaire)
230          seeMetadataResource((Questionnaire) m, questionnaires, true);
231        else if (r instanceof ConceptMap)
232          seeMetadataResource((ConceptMap) m, maps, false);
233        else if (r instanceof StructureMap)
234          seeMetadataResource((StructureMap) m, transforms, false);
235        else if (r instanceof NamingSystem)
236          systems.add((NamingSystem) r);
237      }
238    }
239  }
240
241  /*
242   * Compare business versions, returning "true" if the candidate newer version is
243   * in fact newer than the oldVersion Comparison will work for strictly numeric
244   * versions as well as multi-level versions separated by ., -, _, : or space
245   * Failing that, it will do unicode-based character ordering. E.g. 1.5.3 <
246   * 1.14.3 2017-3-10 < 2017-12-7 A3 < T2
247   */
248  private boolean laterVersion(String newVersion, String oldVersion) {
249    // Compare business versions, retur
250    newVersion = newVersion.trim();
251    oldVersion = oldVersion.trim();
252    if (StringUtils.isNumeric(newVersion) && StringUtils.isNumeric(oldVersion))
253      return Double.parseDouble(newVersion) > Double.parseDouble(oldVersion);
254    else if (hasDelimiter(newVersion, oldVersion, "."))
255      return laterDelimitedVersion(newVersion, oldVersion, "\\.");
256    else if (hasDelimiter(newVersion, oldVersion, "-"))
257      return laterDelimitedVersion(newVersion, oldVersion, "\\-");
258    else if (hasDelimiter(newVersion, oldVersion, "_"))
259      return laterDelimitedVersion(newVersion, oldVersion, "\\_");
260    else if (hasDelimiter(newVersion, oldVersion, ":"))
261      return laterDelimitedVersion(newVersion, oldVersion, "\\:");
262    else if (hasDelimiter(newVersion, oldVersion, " "))
263      return laterDelimitedVersion(newVersion, oldVersion, "\\ ");
264    else {
265      return newVersion.compareTo(oldVersion) > 0;
266    }
267  }
268
269  /*
270   * Returns true if both strings include the delimiter and have the same number
271   * of occurrences of it
272   */
273  private boolean hasDelimiter(String s1, String s2, String delimiter) {
274    return s1.contains(delimiter) && s2.contains(delimiter) && s1.split(delimiter).length == s2.split(delimiter).length;
275  }
276
277  private boolean laterDelimitedVersion(String newVersion, String oldVersion, String delimiter) {
278    String[] newParts = newVersion.split(delimiter);
279    String[] oldParts = oldVersion.split(delimiter);
280    for (int i = 0; i < newParts.length; i++) {
281      if (!newParts[i].equals(oldParts[i]))
282        return laterVersion(newParts[i], oldParts[i]);
283    }
284    // This should never happen
285    throw new Error(
286        "Delimited versions have exact match for delimiter '" + delimiter + "' : " + newParts + " vs " + oldParts);
287  }
288
289  protected <T extends MetadataResource> void seeMetadataResource(T r, Map<String, T> map, boolean addId)
290      throws FHIRException {
291    if (addId)
292      map.put(r.getId(), r); // todo: why?
293    if (!map.containsKey(r.getUrl()))
294      map.put(r.getUrl(), r);
295    else {
296      // If this resource already exists, see if it's the newest business version. The
297      // default resource to return if not qualified is the most recent business
298      // version
299      MetadataResource existingResource = (MetadataResource) map.get(r.getUrl());
300      if (r.hasVersion() && existingResource.hasVersion() && !r.getVersion().equals(existingResource.getVersion())) {
301        if (laterVersion(r.getVersion(), existingResource.getVersion())) {
302          map.remove(r.getUrl());
303          map.put(r.getUrl(), r);
304        }
305      } else
306        map.remove(r.getUrl());
307      map.put(r.getUrl(), r);
308//        throw new FHIRException("Multiple declarations of resource with same canonical URL (" + r.getUrl() + ") and version (" + (r.hasVersion() ? r.getVersion() : "" ) + ")");
309    }
310    if (r.hasVersion())
311      map.put(r.getUrl() + "|" + r.getVersion(), r);
312  }
313
314  @Override
315  public CodeSystem fetchCodeSystem(String system) {
316    synchronized (lock) {
317      return codeSystems.get(system);
318    }
319  }
320
321  @Override
322  public boolean supportsSystem(String system) throws TerminologyServiceException {
323    synchronized (lock) {
324      if (codeSystems.containsKey(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT)
325        return true;
326      else if (supportedCodeSystems.contains(system))
327        return true;
328      else if (system.startsWith("http://example.org") || system.startsWith("http://acme.com")
329          || system.startsWith("http://hl7.org/fhir/valueset-") || system.startsWith("urn:oid:"))
330        return false;
331      else {
332        if (noTerminologyServer)
333          return false;
334        if (txcaps == null) {
335          try {
336            log.info("Terminology server: Check for supported code systems for " + system);
337            txcaps = txClient.getTerminologyCapabilities();
338          } catch (Exception e) {
339            if (canRunWithoutTerminology) {
340              noTerminologyServer = true;
341              log.info("==============!! Running without terminology server !! ==============");
342              if (txClient != null) {
343                log.info("txServer = " + txClient.getAddress());
344                log.info("Error = " + e.getMessage() + "");
345              }
346              log.info("=====================================================================");
347              return false;
348            } else
349              throw new TerminologyServiceException(e);
350          }
351          if (txcaps != null) {
352            for (TerminologyCapabilitiesCodeSystemComponent tccs : txcaps.getCodeSystem()) {
353              supportedCodeSystems.add(tccs.getUri());
354            }
355          }
356          if (supportedCodeSystems.contains(system))
357            return true;
358        }
359      }
360      return false;
361    }
362  }
363
364  protected void tlog(String msg) {
365    if (tlogging)
366      log.info("-tx cache miss: " + msg);
367  }
368
369  // --- expansion support
370  // ------------------------------------------------------------------------------------------------------------
371
372  public int getExpandCodesLimit() {
373    return expandCodesLimit;
374  }
375
376  public void setExpandCodesLimit(int expandCodesLimit) {
377    this.expandCodesLimit = expandCodesLimit;
378  }
379
380  @Override
381  public ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent binding, boolean cacheOk,
382      boolean heirarchical) throws FHIRException {
383    ValueSet vs = null;
384    vs = fetchResource(ValueSet.class, binding.getValueSet());
385    if (vs == null)
386      throw new FHIRException("Unable to resolve value Set " + binding.getValueSet());
387    return expandVS(vs, cacheOk, heirarchical);
388  }
389
390  @Override
391  public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean heirachical)
392      throws TerminologyServiceException {
393    ValueSet vs = new ValueSet();
394    vs.setCompose(new ValueSetComposeComponent());
395    vs.getCompose().getInclude().add(inc);
396    CacheToken cacheToken = txCache.generateExpandToken(vs, heirachical);
397    ValueSetExpansionOutcome res;
398    res = txCache.getExpansion(cacheToken);
399    if (res != null)
400      return res;
401    Parameters p = expParameters.copy();
402    p.setParameter("includeDefinition", false);
403    p.setParameter("excludeNested", !heirachical);
404
405    if (noTerminologyServer)
406      return new ValueSetExpansionOutcome("Error expanding ValueSet: running without terminology services",
407          TerminologyServiceErrorClass.NOSERVICE);
408
409    p.addParameter().setName("_limit").setValue(new IntegerType(expandCodesLimit));
410    p.addParameter().setName("_incomplete").setValue(new BooleanType("true"));
411    
412    tlog("$expand on " + txCache.summary(vs));
413    try {
414      ValueSet result = txClient.expandValueset(vs, p);
415      res = new ValueSetExpansionOutcome(result).setTxLink(txLog == null ? null : txLog.getLastId());
416    } catch (Exception e) {
417      res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
418          TerminologyServiceErrorClass.UNKNOWN);
419      if (txLog != null)
420        res.setTxLink(txLog.getLastId());
421    }
422    txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
423    return res;
424
425  }
426
427  @Override
428  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical) {
429    if (expParameters == null)
430      throw new Error("No Expansion Parameters provided");
431    Parameters p = expParameters.copy();
432    return expandVS(vs, cacheOk, heirarchical, p);
433  }
434
435  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, Parameters p) {
436    if (p == null)
437      throw new Error("No Parameters provided to expandVS");
438    if (vs.hasExpansion()) {
439      return new ValueSetExpansionOutcome(vs.copy());
440    }
441    if (!vs.hasUrl())
442      throw new Error("no value set");
443
444    CacheToken cacheToken = txCache.generateExpandToken(vs, heirarchical);
445    ValueSetExpansionOutcome res;
446    if (cacheOk) {
447      res = txCache.getExpansion(cacheToken);
448      if (res != null)
449        return res;
450    }
451    p.setParameter("includeDefinition", false);
452    p.setParameter("excludeNested", !heirarchical);
453
454    // ok, first we try to expand locally
455    try {
456      ValueSetExpanderSimple vse = new ValueSetExpanderSimple(this);
457      res = vse.doExpand(vs, p);
458      if (!res.getValueset().hasUrl())
459        throw new Error("no url in expand value set");
460      txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT);
461      return res;
462    } catch (Exception e) {
463    }
464
465    // if that failed, we try to expand on the server
466    if (noTerminologyServer)
467      return new ValueSetExpansionOutcome("Error expanding ValueSet: running without terminology services",
468          TerminologyServiceErrorClass.NOSERVICE);
469
470    p.addParameter().setName("_limit").setValue(new IntegerType(expandCodesLimit));
471    p.addParameter().setName("_incomplete").setValue(new BooleanType("true"));
472    
473    tlog("$expand on " + txCache.summary(vs));
474    try {
475      ValueSet result = txClient.expandValueset(vs, p);
476      if (result != null) {
477        if (!result.hasUrl())
478          result.setUrl(vs.getUrl());
479        if (!result.hasUrl())
480          throw new Error("no url in expand value set 2");
481      }
482      res = new ValueSetExpansionOutcome(result).setTxLink(txLog == null ? null : txLog.getLastId());
483    } catch (Exception e) {
484      res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
485          TerminologyServiceErrorClass.UNKNOWN).setTxLink(txLog == null ? null : txLog.getLastId());
486    }
487    txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
488    return res;
489  }
490
491  private boolean hasTooCostlyExpansion(ValueSet valueset) {
492    return valueset != null && valueset.hasExpansion() && ToolingExtensions.hasExtension(valueset.getExpansion(),
493        "http://hl7.org/fhir/StructureDefinition/valueset-toocostly");
494  }
495  // --- validate code
496  // -------------------------------------------------------------------------------
497
498  @Override
499  public ValidationResult validateCode(ValidationOptions options, String system, String code, String display) {
500    Coding c = new Coding(system, code, display);
501    return validateCode(options, c, null);
502  }
503
504  @Override
505  public ValidationResult validateCode(ValidationOptions options, String system, String code, String display,
506      ValueSet vs) {
507    Coding c = new Coding(system, code, display);
508    return validateCode(options, c, vs);
509  }
510
511  @Override
512  public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) {
513    Coding c = new Coding(null, code, null);
514    return doValidateCode(options, c, vs, true);
515  }
516
517  @Override
518  public ValidationResult validateCode(ValidationOptions options, String system, String code, String display,
519      ConceptSetComponent vsi) {
520    Coding c = new Coding(system, code, display);
521    ValueSet vs = new ValueSet();
522    vs.setUrl(UUIDUtilities.makeUuidUrn());
523    vs.getCompose().addInclude(vsi);
524    return validateCode(options, c, vs);
525  }
526
527  @Override
528  public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
529    return doValidateCode(options, code, vs, false);
530  }
531
532  public ValidationResult doValidateCode(ValidationOptions options, Coding code, ValueSet vs, boolean inferSystem) {
533    CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
534    ValidationResult res = null;
535    if (txCache != null)
536      res = txCache.getValidation(cacheToken);
537    if (res != null)
538      return res;
539
540    // ok, first we try to validate locally
541    try {
542      ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
543      res = vsc.validateCode(code);
544      if (txCache != null)
545        txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
546      return res;
547    } catch (Exception e) {
548    }
549
550    // if that failed, we try to validate on the server
551    if (noTerminologyServer)
552      return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services",
553          TerminologyServiceErrorClass.NOSERVICE);
554    String csumm = txCache != null ? txCache.summary(code) : null;
555    if (txCache != null)
556      tlog("$validate " + csumm + " for " + txCache.summary(vs));
557    else
558      tlog("$validate " + csumm + " before cache exists");
559    try {
560      Parameters pIn = new Parameters();
561      pIn.addParameter().setName("coding").setValue(code);
562      if (inferSystem)
563        pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
564      if (options != null)
565        setTerminologyOptions(options, pIn);
566      res = validateOnServer(vs, pIn);
567    } catch (Exception e) {
568      res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage())
569          .setTxLink(txLog == null ? null : txLog.getLastId());
570    }
571    if (txCache != null)
572      txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
573    return res;
574  }
575
576  private void setTerminologyOptions(ValidationOptions options, Parameters pIn) {
577    if (options != null && options.hasLanguages()) {
578      pIn.addParameter("displayLanguage", options.getLanguages().toString());
579    }
580  }
581
582  @Override
583  public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) {
584    CacheToken cacheToken = txCache.generateValidationToken(options, code, vs);
585    ValidationResult res = txCache.getValidation(cacheToken);
586    if (res != null)
587      return res;
588
589    // ok, first we try to validate locally
590    try {
591      ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
592      res = vsc.validateCode(code);
593      txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
594      return res;
595    } catch (Exception e) {
596    }
597
598    // if that failed, we try to validate on the server
599    if (noTerminologyServer)
600      return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services",
601          TerminologyServiceErrorClass.NOSERVICE);
602    tlog("$validate " + txCache.summary(code) + " for " + txCache.summary(vs));
603    try {
604      Parameters pIn = new Parameters();
605      pIn.addParameter().setName("codeableConcept").setValue(code);
606      if (options != null)
607        setTerminologyOptions(options, pIn);
608      res = validateOnServer(vs, pIn);
609    } catch (Exception e) {
610      res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage())
611          .setTxLink(txLog == null ? null : txLog.getLastId());
612    }
613    txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
614    return res;
615  }
616
617  private ValidationResult validateOnServer(ValueSet vs, Parameters pin) throws FHIRException {
618    if (vs != null)
619      pin.addParameter().setName("valueSet").setResource(vs);
620    for (ParametersParameterComponent pp : pin.getParameter())
621      if (pp.getName().equals("profile"))
622        throw new Error("Can only specify profile in the context");
623    if (expParameters == null)
624      throw new Error("No ExpansionProfile provided");
625    pin.addParameter().setName("profile").setResource(expParameters);
626    txLog.clearLastId();
627    Parameters pOut;
628    if (vs == null)
629      pOut = txClient.validateCS(pin);
630    else
631      pOut = txClient.validateVS(pin);
632    boolean ok = false;
633    String message = "No Message returned";
634    String display = null;
635    TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN;
636    for (ParametersParameterComponent p : pOut.getParameter()) {
637      if (p.getName().equals("result"))
638        ok = ((BooleanType) p.getValue()).getValue().booleanValue();
639      else if (p.getName().equals("message"))
640        message = ((StringType) p.getValue()).getValue();
641      else if (p.getName().equals("display"))
642        display = ((StringType) p.getValue()).getValue();
643      else if (p.getName().equals("cause")) {
644        try {
645          IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue());
646          if (it == IssueType.UNKNOWN)
647            err = TerminologyServiceErrorClass.UNKNOWN;
648          else if (it == IssueType.NOTSUPPORTED)
649            err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED;
650        } catch (FHIRException e) {
651        }
652      }
653    }
654    if (!ok)
655      return new ValidationResult(IssueSeverity.ERROR, message, err).setTxLink(txLog == null ? null : txLog.getLastId());
656    else if (message != null && !message.equals("No Message returned"))
657      return new ValidationResult(IssueSeverity.WARNING, message, new ConceptDefinitionComponent().setDisplay(display)).setTxLink(txLog == null ? null : txLog.getLastId());
658    else if (display != null)
659      return new ValidationResult(new ConceptDefinitionComponent().setDisplay(display)).setTxLink(txLog == null ? null : txLog.getLastId());
660    else
661      return new ValidationResult(new ConceptDefinitionComponent()).setTxLink(txLog == null ? null : txLog.getLastId());
662  }
663
664  // --------------------------------------------------------------------------------------------------------------------------------------------------------
665
666  public void initTS(String cachePath) throws Exception {
667    txCache = new TerminologyCache(lock, cachePath);
668  }
669
670  @Override
671  public List<ConceptMap> findMapsForSource(String url) throws FHIRException {
672    synchronized (lock) {
673      List<ConceptMap> res = new ArrayList<ConceptMap>();
674      for (ConceptMap map : maps.values())
675        if (((Reference) map.getSource()).getReference().equals(url))
676          res.add(map);
677      return res;
678    }
679  }
680
681  public boolean isCanRunWithoutTerminology() {
682    return canRunWithoutTerminology;
683  }
684
685  public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) {
686    this.canRunWithoutTerminology = canRunWithoutTerminology;
687  }
688
689  @Deprecated
690  public void setLogger(ILoggingService logger) {
691
692  }
693
694  public Parameters getExpansionParameters() {
695    return expParameters;
696  }
697
698  public void setExpansionProfile(Parameters expParameters) {
699    this.expParameters = expParameters;
700  }
701
702  @Override
703  public boolean isNoTerminologyServer() {
704    return noTerminologyServer;
705  }
706
707  public String getName() {
708    return name;
709  }
710
711  public void setName(String name) {
712    this.name = name;
713  }
714
715  @Override
716  public Set<String> getResourceNamesAsSet() {
717    Set<String> res = new HashSet<String>();
718    res.addAll(getResourceNames());
719    return res;
720  }
721
722  public boolean isAllowLoadingDuplicates() {
723    return allowLoadingDuplicates;
724  }
725
726  public void setAllowLoadingDuplicates(boolean allowLoadingDuplicates) {
727    this.allowLoadingDuplicates = allowLoadingDuplicates;
728  }
729
730  @SuppressWarnings("unchecked")
731  @Override
732  public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException {
733    if (class_ == StructureDefinition.class)
734      uri = ProfileUtilities.sdNs(uri, getOverrideVersionNs());
735    synchronized (lock) {
736
737      if (uri.startsWith("http:") || uri.startsWith("https:")) {
738        String version = null;
739        if (uri.contains("|")) {
740          version = uri.substring(uri.lastIndexOf("|") + 1);
741          uri = uri.substring(0, uri.lastIndexOf("|"));
742        }
743        if (uri.contains("#"))
744          uri = uri.substring(0, uri.indexOf("#"));
745        if (class_ == Resource.class || class_ == null) {
746          if (structures.containsKey(uri))
747            return (T) structures.get(uri);
748          if (guides.containsKey(uri))
749            return (T) guides.get(uri);
750          if (capstmts.containsKey(uri))
751            return (T) capstmts.get(uri);
752          if (valueSets.containsKey(uri))
753            return (T) valueSets.get(uri);
754          if (codeSystems.containsKey(uri))
755            return (T) codeSystems.get(uri);
756          if (operations.containsKey(uri))
757            return (T) operations.get(uri);
758          if (searchParameters.containsKey(uri))
759            return (T) searchParameters.get(uri);
760          if (plans.containsKey(uri))
761            return (T) plans.get(uri);
762          if (maps.containsKey(uri))
763            return (T) maps.get(uri);
764          if (transforms.containsKey(uri))
765            return (T) transforms.get(uri);
766          if (questionnaires.containsKey(uri))
767            return (T) questionnaires.get(uri);
768          for (Map<String, Resource> rt : allResourcesById.values()) {
769            for (Resource r : rt.values()) {
770              if (r instanceof MetadataResource) {
771                MetadataResource mr = (MetadataResource) r;
772                if (uri.equals(mr.getUrl()))
773                  return (T) mr;
774              }
775            }
776          }
777          return null;
778        } else if (class_ == ImplementationGuide.class) {
779          return (T) guides.get(uri);
780        } else if (class_ == CapabilityStatement.class) {
781          return (T) capstmts.get(uri);
782        } else if (class_ == StructureDefinition.class) {
783          return (T) structures.get(uri);
784        } else if (class_ == StructureMap.class) {
785          return (T) transforms.get(uri);
786        } else if (class_ == ValueSet.class) {
787          if (valueSets.containsKey(uri + "|" + version))
788            return (T) valueSets.get(uri + "|" + version);
789          else
790            return (T) valueSets.get(uri);
791        } else if (class_ == CodeSystem.class) {
792          if (codeSystems.containsKey(uri + "|" + version))
793            return (T) codeSystems.get(uri + "|" + version);
794          else
795            return (T) codeSystems.get(uri);
796        } else if (class_ == ConceptMap.class) {
797          return (T) maps.get(uri);
798        } else if (class_ == PlanDefinition.class) {
799          return (T) plans.get(uri);
800        } else if (class_ == OperationDefinition.class) {
801          OperationDefinition od = operations.get(uri);
802          return (T) od;
803        } else if (class_ == SearchParameter.class) {
804          SearchParameter res = searchParameters.get(uri);
805          if (res == null) {
806            StringBuilder b = new StringBuilder();
807            for (String s : searchParameters.keySet()) {
808              b.append(s);
809              b.append("\r\n");
810            }
811          }
812          return (T) res;
813        }
814      }
815      if (class_ == CodeSystem.class && codeSystems.containsKey(uri))
816        return (T) codeSystems.get(uri);
817
818      if (class_ == Questionnaire.class)
819        return (T) questionnaires.get(uri);
820      if (class_ == null) {
821        if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet"))
822          return null;
823
824        // it might be a special URL.
825        if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
826          Resource res = null; // findTxValueSet(uri);
827          if (res != null)
828            return (T) res;
829        }
830        return null;
831      }
832      if (supportedCodeSystems.contains(uri))
833        return null;
834      throw new FHIRException("not done yet: can't fetch " + uri);
835    }
836  }
837
838  private Set<String> notCanonical = new HashSet<String>();
839
840  private String overrideVersionNs;
841
842//  private MetadataResource findTxValueSet(String uri) {
843//    MetadataResource res = expansionCache.getStoredResource(uri);
844//    if (res != null)
845//      return res;
846//    synchronized (lock) {
847//      if (notCanonical.contains(uri))
848//        return null;
849//    }
850//    try {
851//      tlog("get canonical "+uri);
852//      res = txServer.getCanonical(ValueSet.class, uri);
853//    } catch (Exception e) {
854//      synchronized (lock) {
855//        notCanonical.add(uri);
856//      }
857//      return null;
858//    }
859//    if (res != null)
860//      try {
861//        expansionCache.storeResource(res);
862//      } catch (IOException e) {
863//      }
864//    return res;
865//  }
866
867  @Override
868  public Resource fetchResourceById(String type, String uri) {
869    synchronized (lock) {
870      String[] parts = uri.split("\\/");
871      if (!Utilities.noString(type) && parts.length == 1) {
872        if (allResourcesById.containsKey(type))
873          return allResourcesById.get(type).get(parts[0]);
874        else
875          return null;
876      }
877      if (parts.length >= 2) {
878        if (!Utilities.noString(type))
879          if (!type.equals(parts[parts.length - 2]))
880            throw new Error("Resource type mismatch for " + type + " / " + uri);
881        return allResourcesById.get(parts[parts.length - 2]).get(parts[parts.length - 1]);
882      } else
883        throw new Error("Unable to process request for resource for " + type + " / " + uri);
884    }
885  }
886
887  public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
888    try {
889      return fetchResourceWithException(class_, uri);
890    } catch (FHIRException e) {
891      throw new Error(e);
892    }
893  }
894
895  @Override
896  public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource source) {
897    return fetchResource(class_, uri);
898  }
899
900  @Override
901  public List<StructureDefinition> fetchTypeDefinitions(String n) {
902    List<StructureDefinition> types = new ArrayList<>();
903    for (StructureDefinition sd : fetchResourcesByType(StructureDefinition.class)) {
904      if (n.equals(sd.getTypeTail())) {
905        types.add(sd);
906      }
907    }
908    return types;
909  }
910
911  public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) {
912    try {
913      return fetchResourceWithException(class_, uri+"|"+version);
914    } catch (FHIRException e) {
915      throw new Error(e);
916    }
917  }
918
919  @Override
920  public <T extends Resource> boolean hasResource(Class<T> class_, String uri) {
921    try {
922      return fetchResourceWithException(class_, uri) != null;
923    } catch (Exception e) {
924      return false;
925    }
926  }
927
928  public TranslationServices translator() {
929    return translator;
930  }
931
932  public void setTranslator(TranslationServices translator) {
933    this.translator = translator;
934  }
935
936  public class NullTranslator implements TranslationServices {
937
938    @Override
939    public String translate(String context, String value, String targetLang) {
940      return value;
941    }
942
943    @Override
944    public String translate(String context, String value) {
945      return value;
946    }
947
948    @Override
949    public String toStr(float value) {
950      return null;
951    }
952
953    @Override
954    public String toStr(Date value) {
955      return null;
956    }
957
958    @Override
959    public String translateAndFormat(String contest, String lang, String value, Object... args) {
960      return String.format(value, args);
961    }
962
963    @Override
964    public Map<String, String> translations(String value) {
965      // TODO Auto-generated method stub
966      return null;
967    }
968
969    @Override
970    public Set<String> listTranslations(String category) {
971      // TODO Auto-generated method stub
972      return null;
973    }
974
975  }
976
977  public void reportStatus(JsonObject json) {
978    synchronized (lock) {
979      json.addProperty("codeystem-count", codeSystems.size());
980      json.addProperty("valueset-count", valueSets.size());
981      json.addProperty("conceptmap-count", maps.size());
982      json.addProperty("transforms-count", transforms.size());
983      json.addProperty("structures-count", structures.size());
984      json.addProperty("guides-count", guides.size());
985      json.addProperty("statements-count", capstmts.size());
986    }
987  }
988
989  public void dropResource(Resource r) throws FHIRException {
990    dropResource(r.fhirType(), r.getId());
991  }
992
993  public void dropResource(String fhirType, String id) {
994    synchronized (lock) {
995
996      Map<String, Resource> map = allResourcesById.get(fhirType);
997      if (map == null) {
998        map = new HashMap<String, Resource>();
999        allResourcesById.put(fhirType, map);
1000      }
1001      if (map.containsKey(id))
1002        map.remove(id);
1003
1004      if (fhirType.equals("StructureDefinition"))
1005        dropMetadataResource(structures, id);
1006      else if (fhirType.equals("ImplementationGuide"))
1007        dropMetadataResource(guides, id);
1008      else if (fhirType.equals("CapabilityStatement"))
1009        dropMetadataResource(capstmts, id);
1010      else if (fhirType.equals("ValueSet"))
1011        dropMetadataResource(valueSets, id);
1012      else if (fhirType.equals("CodeSystem"))
1013        dropMetadataResource(codeSystems, id);
1014      else if (fhirType.equals("OperationDefinition"))
1015        dropMetadataResource(operations, id);
1016      else if (fhirType.equals("Questionnaire"))
1017        dropMetadataResource(questionnaires, id);
1018      else if (fhirType.equals("ConceptMap"))
1019        dropMetadataResource(maps, id);
1020      else if (fhirType.equals("StructureMap"))
1021        dropMetadataResource(transforms, id);
1022      else if (fhirType.equals("NamingSystem"))
1023        for (int i = systems.size() - 1; i >= 0; i--) {
1024          if (systems.get(i).getId().equals(id))
1025            systems.remove(i);
1026        }
1027    }
1028  }
1029
1030  private <T extends MetadataResource> void dropMetadataResource(Map<String, T> map, String id) {
1031    T res = map.get(id);
1032    if (res != null) {
1033      map.remove(id);
1034      if (map.containsKey(res.getUrl()))
1035        map.remove(res.getUrl());
1036      if (res.getVersion() != null)
1037        if (map.containsKey(res.getUrl() + "|" + res.getVersion()))
1038          map.remove(res.getUrl() + "|" + res.getVersion());
1039    }
1040  }
1041
1042  @Override
1043  public List<MetadataResource> allConformanceResources() {
1044    synchronized (lock) {
1045      List<MetadataResource> result = new ArrayList<MetadataResource>();
1046      result.addAll(structures.values());
1047      result.addAll(guides.values());
1048      result.addAll(capstmts.values());
1049      result.addAll(codeSystems.values());
1050      result.addAll(valueSets.values());
1051      result.addAll(maps.values());
1052      result.addAll(transforms.values());
1053      result.addAll(plans.values());
1054      result.addAll(questionnaires.values());
1055      return result;
1056    }
1057  }
1058
1059  public String listSupportedSystems() {
1060    synchronized (lock) {
1061      String sl = null;
1062      for (String s : supportedCodeSystems)
1063        sl = sl == null ? s : sl + "\r\n" + s;
1064      return sl;
1065    }
1066  }
1067
1068  public int totalCount() {
1069    synchronized (lock) {
1070      return valueSets.size() + maps.size() + structures.size() + transforms.size();
1071    }
1072  }
1073
1074  public List<ConceptMap> listMaps() {
1075    List<ConceptMap> m = new ArrayList<ConceptMap>();
1076    synchronized (lock) {
1077      m.addAll(maps.values());
1078    }
1079    return m;
1080  }
1081
1082  public List<StructureMap> listTransforms() {
1083    List<StructureMap> m = new ArrayList<StructureMap>();
1084    synchronized (lock) {
1085      m.addAll(transforms.values());
1086    }
1087    return m;
1088  }
1089
1090  public StructureMap getTransform(String code) {
1091    synchronized (lock) {
1092      return transforms.get(code);
1093    }
1094  }
1095
1096  public List<StructureDefinition> listStructures() {
1097    List<StructureDefinition> m = new ArrayList<StructureDefinition>();
1098    synchronized (lock) {
1099      m.addAll(structures.values());
1100    }
1101    return m;
1102  }
1103
1104  public StructureDefinition getStructure(String code) {
1105    synchronized (lock) {
1106      return structures.get(code);
1107    }
1108  }
1109
1110  @Override
1111  public String oid2Uri(String oid) {
1112    synchronized (lock) {
1113      String uri = OIDUtilities.getUriForOid(oid);
1114      if (uri != null)
1115        return uri;
1116      for (NamingSystem ns : systems) {
1117        if (hasOid(ns, oid)) {
1118          uri = getUri(ns);
1119          if (uri != null)
1120            return null;
1121        }
1122      }
1123    }
1124    return null;
1125  }
1126
1127  private String getUri(NamingSystem ns) {
1128    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
1129      if (id.getType() == NamingSystemIdentifierType.URI)
1130        return id.getValue();
1131    }
1132    return null;
1133  }
1134
1135  private boolean hasOid(NamingSystem ns, String oid) {
1136    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
1137      if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid))
1138        return true;
1139    }
1140    return false;
1141  }
1142
1143  public void cacheVS(JsonObject json, Map<String, ValidationResult> t) {
1144    synchronized (lock) {
1145      validationCache.put(json.get("url").getAsString(), t);
1146    }
1147  }
1148
1149  public SearchParameter getSearchParameter(String code) {
1150    synchronized (lock) {
1151      return searchParameters.get(code);
1152    }
1153  }
1154
1155  @Override
1156  public String getOverrideVersionNs() {
1157    return overrideVersionNs;
1158  }
1159
1160  @Override
1161  public void setOverrideVersionNs(String value) {
1162    overrideVersionNs = value;
1163  }
1164
1165  @Override
1166  @Deprecated
1167  public ILoggingService getLogger() {
1168    return null;
1169  }
1170
1171  @Override
1172  public StructureDefinition fetchTypeDefinition(String typeName) {
1173    if (Utilities.isAbsoluteUrl(typeName))
1174      return fetchResource(StructureDefinition.class, typeName);
1175    else
1176      return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
1177  }
1178
1179
1180  public boolean isPrimitiveType(String type) {
1181    return Utilities.existsInList(type, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "uuid", "xhtml", "url", "canonical");
1182  }
1183
1184  public boolean isDataType(String type) {
1185    return Utilities.existsInList(type, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing", 
1186        "ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext");
1187  }
1188  
1189  
1190  public boolean isTlogging() {
1191    return tlogging;
1192  }
1193
1194  public void setTlogging(boolean tlogging) {
1195    this.tlogging = tlogging;
1196  }
1197
1198  public UcumService getUcumService() {
1199    return ucumService;
1200  }
1201
1202  public void setUcumService(UcumService ucumService) {
1203    this.ucumService = ucumService;
1204  }
1205
1206  @Override
1207  public List<StructureDefinition> getStructures() {
1208    List<StructureDefinition> res = new ArrayList<>();
1209    synchronized (lock) { // tricky, because you need to lock this as well, but it's really not in use yet
1210      res.addAll(structures.values());
1211    }
1212    return res;
1213  }
1214
1215  public String getLinkForUrl(String corePath, String url) {
1216    for (CodeSystem r : codeSystems.values())
1217      if (url.equals(r.getUrl()))
1218        return r.getUserString("path");
1219
1220    for (ValueSet r : valueSets.values())
1221      if (url.equals(r.getUrl()))
1222        return r.getUserString("path");
1223
1224    for (ConceptMap r : maps.values())
1225      if (url.equals(r.getUrl()))
1226        return r.getUserString("path");
1227
1228    for (StructureMap r : transforms.values())
1229      if (url.equals(r.getUrl()))
1230        return r.getUserString("path");
1231
1232    for (StructureDefinition r : structures.values())
1233      if (url.equals(r.getUrl()))
1234        return r.getUserString("path");
1235
1236    for (ImplementationGuide r : guides.values())
1237      if (url.equals(r.getUrl()))
1238        return r.getUserString("path");
1239
1240    for (CapabilityStatement r : capstmts.values())
1241      if (url.equals(r.getUrl()))
1242        return r.getUserString("path");
1243
1244    for (SearchParameter r : searchParameters.values())
1245      if (url.equals(r.getUrl()))
1246        return r.getUserString("path");
1247
1248    for (Questionnaire r : questionnaires.values())
1249      if (url.equals(r.getUrl()))
1250        return r.getUserString("path");
1251
1252    for (OperationDefinition r : operations.values())
1253      if (url.equals(r.getUrl()))
1254        return r.getUserString("path");
1255
1256    for (PlanDefinition r : plans.values())
1257      if (url.equals(r.getUrl()))
1258        return r.getUserString("path");
1259
1260    if (url.equals("http://loinc.org"))
1261      return corePath + "loinc.html";
1262    if (url.equals("http://unitsofmeasure.org"))
1263      return corePath + "ucum.html";
1264    if (url.equals("http://snomed.info/sct"))
1265      return corePath + "snomed.html";
1266    return null;
1267  }
1268
1269  @SuppressWarnings("unchecked")
1270  public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_) {
1271
1272    List<T> res = new ArrayList<>();
1273
1274    synchronized (lock) {
1275
1276      if (class_ == Resource.class || class_ == DomainResource.class || class_ == null) {
1277        res.addAll((Collection<T>) structures.values());
1278        res.addAll((Collection<T>) guides.values());
1279        res.addAll((Collection<T>) capstmts.values());
1280        res.addAll((Collection<T>) valueSets.values());
1281        res.addAll((Collection<T>) codeSystems.values());
1282        res.addAll((Collection<T>) operations.values());
1283        res.addAll((Collection<T>) searchParameters.values());
1284        res.addAll((Collection<T>) plans.values());
1285        res.addAll((Collection<T>) maps.values());
1286        res.addAll((Collection<T>) transforms.values());
1287        res.addAll((Collection<T>) questionnaires.values());
1288      } else if (class_ == ImplementationGuide.class) {
1289        res.addAll((Collection<T>) guides.values());
1290      } else if (class_ == CapabilityStatement.class) {
1291        res.addAll((Collection<T>) capstmts.values());
1292      } else if (class_ == StructureDefinition.class) {
1293        res.addAll((Collection<T>) structures.values());
1294      } else if (class_ == StructureMap.class) {
1295        res.addAll((Collection<T>) transforms.values());
1296      } else if (class_ == ValueSet.class) {
1297        res.addAll((Collection<T>) valueSets.values());
1298      } else if (class_ == CodeSystem.class) {
1299        res.addAll((Collection<T>) codeSystems.values());
1300      } else if (class_ == ConceptMap.class) {
1301        res.addAll((Collection<T>) maps.values());
1302      } else if (class_ == PlanDefinition.class) {
1303        res.addAll((Collection<T>) plans.values());
1304      } else if (class_ == OperationDefinition.class) {
1305        res.addAll((Collection<T>) operations.values());
1306      } else if (class_ == Questionnaire.class) {
1307        res.addAll((Collection<T>) questionnaires.values());
1308      } else if (class_ == SearchParameter.class) {
1309        res.addAll((Collection<T>) searchParameters.values());
1310      }
1311    }
1312    return res;
1313  }
1314
1315}