001package org.hl7.fhir.r5.context;
002
003import java.io.File;
004
005/*
006  Copyright (c) 2011+, HL7, Inc.
007  All rights reserved.
008  
009  Redistribution and use in source and binary forms, with or without modification, 
010  are permitted provided that the following conditions are met:
011    
012   * Redistributions of source code must retain the above copyright notice, this 
013     list of conditions and the following disclaimer.
014   * Redistributions in binary form must reproduce the above copyright notice, 
015     this list of conditions and the following disclaimer in the documentation 
016     and/or other materials provided with the distribution.
017   * Neither the name of HL7 nor the names of its contributors may be used to 
018     endorse or promote products derived from this software without specific 
019     prior written permission.
020  
021  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
022  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
023  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
024  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
025  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
026  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
027  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
028  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
029  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
030  POSSIBILITY OF SUCH DAMAGE.
031  
032 */
033
034
035import java.io.FileNotFoundException;
036import java.io.IOException;
037import java.sql.Connection;
038import java.sql.DriverManager;
039import java.sql.PreparedStatement;
040import java.sql.ResultSet;
041import java.util.ArrayList;
042import java.util.Collections;
043import java.util.Comparator;
044import java.util.Date;
045import java.util.HashMap;
046import java.util.HashSet;
047import java.util.List;
048import java.util.Locale;
049import java.util.Map;
050import java.util.Set;
051import java.util.UUID;
052
053import lombok.Getter;
054import org.apache.commons.lang3.StringUtils;
055import org.fhir.ucum.UcumService;
056import org.hl7.fhir.exceptions.DefinitionException;
057import org.hl7.fhir.exceptions.FHIRException;
058import org.hl7.fhir.exceptions.TerminologyServiceException;
059import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
060import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
061import org.hl7.fhir.r5.context.ILoggingService.LogCategory;
062import org.hl7.fhir.r5.model.ActorDefinition;
063import org.hl7.fhir.r5.model.BooleanType;
064import org.hl7.fhir.r5.model.Bundle;
065import org.hl7.fhir.r5.model.CanonicalResource;
066import org.hl7.fhir.r5.model.CanonicalType;
067import org.hl7.fhir.r5.model.CapabilityStatement;
068import org.hl7.fhir.r5.model.CodeSystem;
069import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
070import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
071import org.hl7.fhir.r5.model.CodeableConcept;
072import org.hl7.fhir.r5.model.Coding;
073import org.hl7.fhir.r5.model.ConceptMap;
074import org.hl7.fhir.r5.model.Constants;
075import org.hl7.fhir.r5.model.DomainResource;
076import org.hl7.fhir.r5.model.ElementDefinition;
077import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
078import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
079import org.hl7.fhir.r5.model.Extension;
080import org.hl7.fhir.r5.model.IdType;
081import org.hl7.fhir.r5.model.Identifier;
082import org.hl7.fhir.r5.model.IntegerType;
083import org.hl7.fhir.r5.model.ImplementationGuide;
084import org.hl7.fhir.r5.model.Library;
085import org.hl7.fhir.r5.model.Measure;
086import org.hl7.fhir.r5.model.NamingSystem;
087import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType;
088import org.hl7.fhir.r5.model.NamingSystem.NamingSystemType;
089import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
090import org.hl7.fhir.r5.model.OperationDefinition;
091import org.hl7.fhir.r5.model.OperationOutcome;
092import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
093import org.hl7.fhir.r5.model.PackageInformation;
094import org.hl7.fhir.r5.model.Parameters;
095import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
096import org.hl7.fhir.r5.model.PlanDefinition;
097import org.hl7.fhir.r5.model.PrimitiveType;
098import org.hl7.fhir.r5.model.Questionnaire;
099import org.hl7.fhir.r5.model.Requirements;
100import org.hl7.fhir.r5.model.Resource;
101import org.hl7.fhir.r5.model.SearchParameter;
102import org.hl7.fhir.r5.model.StringType;
103import org.hl7.fhir.r5.model.StructureDefinition;
104import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
105import org.hl7.fhir.r5.model.StructureMap;
106
107import org.hl7.fhir.r5.model.UriType;
108import org.hl7.fhir.r5.model.UrlType;
109import org.hl7.fhir.r5.model.ValueSet;
110import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
111import org.hl7.fhir.r5.model.Bundle.BundleType;
112import org.hl7.fhir.r5.model.Bundle.HTTPVerb;
113import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
114import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
115import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;
116import org.hl7.fhir.r5.profilemodel.PEBuilder;
117import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer;
118import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
119import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander;
120import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
121import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest;
122import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
123import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
124import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
125import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
126import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
127import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.CacheToken;
128import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedCodeSystem;
129import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
130import org.hl7.fhir.r5.terminologies.validation.VSCheckerException;
131import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
132import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
133import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
134import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
135import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext;
136import org.hl7.fhir.r5.utils.PackageHackerR5;
137import org.hl7.fhir.r5.utils.ResourceUtilities;
138import org.hl7.fhir.r5.utils.ToolingExtensions;
139import org.hl7.fhir.r5.utils.client.EFhirClientException;
140import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
141import org.hl7.fhir.utilities.FhirPublication;
142import org.hl7.fhir.utilities.TimeTracker;
143import org.hl7.fhir.utilities.ToolingClientLogger;
144import org.hl7.fhir.utilities.Utilities;
145import org.hl7.fhir.utilities.VersionUtilities;
146import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
147import org.hl7.fhir.utilities.i18n.I18nBase;
148import org.hl7.fhir.utilities.i18n.I18nConstants;
149import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
150import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
151import org.hl7.fhir.utilities.validation.ValidationOptions;
152
153import com.google.gson.JsonObject;
154
155import javax.annotation.Nonnull;
156
157public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext {
158
159  class OIDSource {
160    private String folder;
161    private Connection db;
162    private String pid;
163    protected OIDSource(String folder, String pid) {
164      super();
165      this.folder = folder;
166      this.pid = pid;
167    }
168    
169  }
170
171  private static final boolean QA_CHECK_REFERENCE_SOURCE = false; // see comments below
172
173  public static class ResourceProxy {
174    private Resource resource;
175    private CanonicalResourceProxy proxy;
176
177    public ResourceProxy(Resource resource) {
178      super();
179      this.resource = resource;
180    }
181    public ResourceProxy(CanonicalResourceProxy proxy) {
182      super();
183      this.proxy = proxy;
184    }
185    
186    public Resource getResource() {
187      return resource != null ? resource : proxy.getResource();
188    }
189      
190    public CanonicalResourceProxy getProxy() {
191      return proxy;
192    }
193    
194    public String getUrl() {
195      if (resource == null) {
196        return proxy.getUrl();
197      } else if (resource instanceof CanonicalResource) {
198        return ((CanonicalResource) resource).getUrl(); 
199      } else {
200        return null;
201      }
202    }
203    
204  }
205
206  public class MetadataResourceVersionComparator<T extends CanonicalResource> implements Comparator<T> {
207
208    final private List<T> list;
209
210    public MetadataResourceVersionComparator(List<T> list) {
211      this.list = list;
212    }
213
214    @Override
215    public int compare(T arg1, T arg2) {
216      String v1 = arg1.getVersion();
217      String v2 = arg2.getVersion();
218      if (v1 == null && v2 == null) {
219        return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order
220      } else if (v1 == null) {
221        return -1;
222      } else if (v2 == null) {
223        return 1;
224      } else {
225        String mm1 = VersionUtilities.getMajMin(v1);
226        String mm2 = VersionUtilities.getMajMin(v2);
227        if (mm1 == null || mm2 == null) {
228          return v1.compareTo(v2);
229        } else {
230          return mm1.compareTo(mm2);
231        }
232      }
233    }
234  }
235
236  private Object lock = new Object(); // used as a lock for the data that follows
237  protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be 
238
239  protected final TerminologyClientManager terminologyClientManager = new TerminologyClientManager(new TerminologyClientR5.TerminologyClientR5Factory(), UUID.randomUUID().toString());
240  private boolean minimalMemory = false;
241
242  private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
243  // all maps are to the full URI
244  private CanonicalResourceManager<CodeSystem> codeSystems = new CanonicalResourceManager<CodeSystem>(false, minimalMemory);
245  private final Set<String> supportedCodeSystems = new HashSet<String>();
246  private final Set<String> unsupportedCodeSystems = new HashSet<String>(); // know that the terminology server doesn't support them
247  private CanonicalResourceManager<ValueSet> valueSets = new CanonicalResourceManager<ValueSet>(false, minimalMemory);
248  private CanonicalResourceManager<ConceptMap> maps = new CanonicalResourceManager<ConceptMap>(false, minimalMemory);
249  protected CanonicalResourceManager<StructureMap> transforms = new CanonicalResourceManager<StructureMap>(false, minimalMemory);
250  private CanonicalResourceManager<StructureDefinition> structures = new CanonicalResourceManager<StructureDefinition>(false, minimalMemory);
251  private TypeManager typeManager = new TypeManager(structures);
252  private final CanonicalResourceManager<Measure> measures = new CanonicalResourceManager<Measure>(false, minimalMemory);
253  private final CanonicalResourceManager<Library> libraries = new CanonicalResourceManager<Library>(false, minimalMemory);
254  private CanonicalResourceManager<ImplementationGuide> guides = new CanonicalResourceManager<ImplementationGuide>(false, minimalMemory);
255  private final CanonicalResourceManager<CapabilityStatement> capstmts = new CanonicalResourceManager<CapabilityStatement>(false, minimalMemory);
256  private final CanonicalResourceManager<SearchParameter> searchParameters = new CanonicalResourceManager<SearchParameter>(false, minimalMemory);
257  private final CanonicalResourceManager<Questionnaire> questionnaires = new CanonicalResourceManager<Questionnaire>(false, minimalMemory);
258  private final CanonicalResourceManager<OperationDefinition> operations = new CanonicalResourceManager<OperationDefinition>(false, minimalMemory);
259  private final CanonicalResourceManager<PlanDefinition> plans = new CanonicalResourceManager<PlanDefinition>(false, minimalMemory);
260  private final CanonicalResourceManager<ActorDefinition> actors = new CanonicalResourceManager<ActorDefinition>(false, minimalMemory);
261  private final CanonicalResourceManager<Requirements> requirements = new CanonicalResourceManager<Requirements>(false, minimalMemory);
262  private final CanonicalResourceManager<NamingSystem> systems = new CanonicalResourceManager<NamingSystem>(false, minimalMemory);
263  private Map<String, NamingSystem> systemUrlMap;
264
265  
266  private UcumService ucumService;
267  protected Map<String, byte[]> binaries = new HashMap<String, byte[]>();
268  protected Map<String, Set<OIDDefinition>> oidCacheManual = new HashMap<>();
269  protected List<OIDSource> oidSources = new ArrayList<>();
270
271  protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String,ValidationResult>>();
272  protected String name;
273  private boolean allowLoadingDuplicates;
274
275  private final Set<String> codeSystemsUsed = new HashSet<>();
276  protected ToolingClientLogger txLog;
277  protected boolean canRunWithoutTerminology;
278  protected boolean noTerminologyServer;
279  private int expandCodesLimit = 1000;
280  protected org.hl7.fhir.r5.context.ILoggingService logger = new SystemOutLoggingService();
281  protected Parameters expParameters;
282  private Map<String, PackageInformation> packages = new HashMap<>();
283
284  @Getter
285  protected TerminologyCache txCache = new TerminologyCache(this, null);
286  protected TimeTracker clock;
287  private boolean tlogging = true;
288  private IWorkerContextManager.ICanonicalResourceLocator locator;
289  protected String userAgent;
290
291  protected BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException {
292    setValidationMessageLanguage(getLocale());
293    clock = new TimeTracker();
294  }
295
296  protected BaseWorkerContext(Locale locale) throws FileNotFoundException, IOException, FHIRException {
297    setValidationMessageLanguage(locale);
298    clock = new TimeTracker();
299  }
300
301  protected BaseWorkerContext(CanonicalResourceManager<CodeSystem> codeSystems, CanonicalResourceManager<ValueSet> valueSets, CanonicalResourceManager<ConceptMap> maps, CanonicalResourceManager<StructureDefinition> profiles,
302      CanonicalResourceManager<ImplementationGuide> guides) throws FileNotFoundException, IOException, FHIRException {
303    this();
304    this.codeSystems = codeSystems;
305    this.valueSets = valueSets;
306    this.maps = maps;
307    this.structures = profiles;
308    this.typeManager = new TypeManager(structures);
309    this.guides = guides;
310    clock = new TimeTracker();
311  }
312
313  protected void copy(BaseWorkerContext other) {
314    synchronized (other.lock) { // tricky, because you need to lock this as well, but it's really not in use yet 
315      allResourcesById.putAll(other.allResourcesById);
316      codeSystems.copy(other.codeSystems);
317      valueSets.copy(other.valueSets);
318      maps.copy(other.maps);
319      transforms.copy(other.transforms);
320      structures.copy(other.structures);
321      typeManager = new TypeManager(structures);
322      searchParameters.copy(other.searchParameters);
323      plans.copy(other.plans);
324      questionnaires.copy(other.questionnaires);
325      operations.copy(other.operations);
326      systems.copy(other.systems);
327      systemUrlMap = null;
328      guides.copy(other.guides);
329      capstmts.copy(other.capstmts);
330      measures.copy(other.measures);
331      libraries.copy(libraries);
332
333      allowLoadingDuplicates = other.allowLoadingDuplicates;
334      name = other.name;
335      txLog = other.txLog;
336      canRunWithoutTerminology = other.canRunWithoutTerminology;
337      noTerminologyServer = other.noTerminologyServer;
338      if (other.txCache != null)
339        txCache = other.txCache; // no copy. for now?
340      expandCodesLimit = other.expandCodesLimit;
341      logger = other.logger;
342      expParameters = other.expParameters;
343      version = other.version;
344      supportedCodeSystems.addAll(other.supportedCodeSystems);
345      unsupportedCodeSystems.addAll(other.unsupportedCodeSystems);
346      codeSystemsUsed.addAll(other.codeSystemsUsed);
347      ucumService = other.ucumService;
348      binaries.putAll(other.binaries);
349      oidSources.addAll(other.oidSources);
350      oidCacheManual.putAll(other.oidCacheManual);
351      validationCache.putAll(other.validationCache);
352      tlogging = other.tlogging;
353      locator = other.locator;
354      userAgent = other.userAgent;
355      terminologyClientManager.copy(other.terminologyClientManager);
356      cachingAllowed = other.cachingAllowed;
357    }
358  }
359  
360  
361  public void cacheResource(Resource r) throws FHIRException {
362    cacheResourceFromPackage(r, null);  
363  }
364  
365
366  public void registerResourceFromPackage(CanonicalResourceProxy r, PackageInformation packageInfo) throws FHIRException {    
367    PackageHackerR5.fixLoadedResource(r, packageInfo);
368
369    synchronized (lock) {
370      if (packageInfo != null) {
371        packages.put(packageInfo.getVID(), packageInfo);
372      }
373      if (r.getId() != null) {
374        Map<String, ResourceProxy> map = allResourcesById.get(r.getType());
375        if (map == null) {
376          map = new HashMap<String, ResourceProxy>();
377          allResourcesById.put(r.getType(), map);
378        }
379        if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) {
380          map.put(r.getId(), new ResourceProxy(r));
381        }
382      }
383
384      String url = r.getUrl();
385      if (!allowLoadingDuplicates && hasResourceVersion(r.getType(), url, r.getVersion()) && !packageInfo.isHTO()) {
386        // special workaround for known problems with existing packages
387        if (Utilities.existsInList(url, "http://hl7.org/fhir/SearchParameter/example")) {
388          return;
389        }
390        CanonicalResource ex = fetchResourceWithException(r.getType(), url);
391        throw new DefinitionException(formatMessage(I18nConstants.DUPLICATE_RESOURCE_, url, r.getVersion(), ex.getVersion(),
392            ex.fhirType()));
393      }
394      switch(r.getType()) {
395      case "StructureDefinition":
396        if ("1.4.0".equals(version)) {
397          StructureDefinition sd = (StructureDefinition) r.getResource();
398          fixOldSD(sd);
399        }
400        structures.register(r, packageInfo);
401        typeManager.see(r);
402        break;
403      case "ValueSet":
404        valueSets.register(r, packageInfo);
405        break;
406      case "CodeSystem":        
407        codeSystems.register(r, packageInfo);
408        break;
409      case "ImplementationGuide":
410        guides.register(r, packageInfo);
411        break;
412      case "CapabilityStatement":
413        capstmts.register(r, packageInfo);
414        break;
415      case "Measure":
416        measures.register(r, packageInfo);
417        break;
418      case "Library":
419        libraries.register(r, packageInfo);
420        break;
421      case "SearchParameter":
422        searchParameters.register(r, packageInfo);
423        break;
424      case "PlanDefinition":
425        plans.register(r, packageInfo);
426        break;
427      case "OperationDefinition":
428        operations.register(r, packageInfo);
429        break;
430      case "Questionnaire":
431        questionnaires.register(r, packageInfo);
432        break;
433      case "ConceptMap":
434        maps.register(r, packageInfo);
435        break;
436      case "StructureMap":
437        transforms.register(r, packageInfo);
438        break;
439      case "NamingSystem":
440        systems.register(r, packageInfo);
441        break;
442      case "Requirements":
443        requirements.register(r, packageInfo);
444        break;
445      case "ActorDefinition":
446        actors.register(r, packageInfo);
447        break;
448      }
449    }
450  }
451
452  public void cacheResourceFromPackage(Resource r, PackageInformation packageInfo) throws FHIRException {
453 
454    synchronized (lock) {   
455      if (packageInfo != null) {
456        packages.put(packageInfo.getVID(), packageInfo);
457      }
458
459      if (r.getId() != null) {
460        Map<String, ResourceProxy> map = allResourcesById.get(r.fhirType());
461        if (map == null) {
462          map = new HashMap<String, ResourceProxy>();
463          allResourcesById.put(r.fhirType(), map);
464        }
465        if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) {
466          map.put(r.getId(), new ResourceProxy(r));
467        } else {
468          logger.logDebugMessage(LogCategory.PROGRESS,"Ignore "+r.fhirType()+"/"+r.getId()+" from package "+packageInfo.toString());
469        }
470      }
471
472      if (r instanceof CodeSystem || r instanceof NamingSystem) {
473        String url = null;
474        Set<String> oids = new HashSet<String>();
475        if (r instanceof CodeSystem) {
476          CodeSystem cs = (CodeSystem) r;
477          url = cs.getUrl();
478          for (Identifier id : cs.getIdentifier()) {
479            if (id.hasValue() && id.getValue().startsWith("urn:oid:")) {
480              oids.add(id.getValue().substring(8));           
481            }
482          }
483        }
484        if (r instanceof NamingSystem) {
485          NamingSystem ns = ((NamingSystem) r);
486          if (ns.getKind() == NamingSystemType.CODESYSTEM) {
487            for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
488              if (id.getType() == NamingSystemIdentifierType.URI) {
489                url = id.getValue();
490              }
491              if (id.getType() == NamingSystemIdentifierType.OID) {
492                oids.add(id.getValue());
493              }
494            }
495          }
496        }
497        if (url != null) {
498          for (String s : oids) {
499            if (!oidCacheManual.containsKey(s)) {
500              oidCacheManual.put(s, new HashSet<>());
501            }
502            oidCacheManual.get(s).add(new OIDDefinition(r.fhirType(), s, url, ((CanonicalResource) r).getVersion(), null, null));
503          }
504        }
505      }
506
507      if (r instanceof CanonicalResource) {
508        CanonicalResource m = (CanonicalResource) r;
509        String url = m.getUrl();
510        if (!allowLoadingDuplicates && hasResource(r.getClass(), url)) {
511          // special workaround for known problems with existing packages
512          if (Utilities.existsInList(url, "http://hl7.org/fhir/SearchParameter/example")) {
513            return;
514          }
515          CanonicalResource ex = (CanonicalResource) fetchResourceWithException(r.getClass(), url);
516          throw new DefinitionException(formatMessage(I18nConstants.DUPLICATE_RESOURCE_, url, ((CanonicalResource) r).getVersion(), ex.getVersion(),
517              ex.fhirType()));
518        }
519        if (r instanceof StructureDefinition) {
520          StructureDefinition sd = (StructureDefinition) m;
521          if ("1.4.0".equals(version)) {
522            fixOldSD(sd);
523          }
524          structures.see(sd, packageInfo);
525          typeManager.see(sd);
526        } else if (r instanceof ValueSet) {
527          valueSets.see((ValueSet) m, packageInfo);
528        } else if (r instanceof CodeSystem) {
529          CodeSystemUtilities.crossLinkCodeSystem((CodeSystem) r);
530          codeSystems.see((CodeSystem) m, packageInfo);
531        } else if (r instanceof ImplementationGuide) {
532          guides.see((ImplementationGuide) m, packageInfo);
533        } else if (r instanceof CapabilityStatement) {
534          capstmts.see((CapabilityStatement) m, packageInfo);
535        } else if (r instanceof Measure) {
536          measures.see((Measure) m, packageInfo);
537        } else if (r instanceof Library) {
538          libraries.see((Library) m, packageInfo);        
539        } else if (r instanceof SearchParameter) {
540          searchParameters.see((SearchParameter) m, packageInfo);
541        } else if (r instanceof PlanDefinition) {
542          plans.see((PlanDefinition) m, packageInfo);
543        } else if (r instanceof OperationDefinition) {
544          operations.see((OperationDefinition) m, packageInfo);
545        } else if (r instanceof Questionnaire) {
546          questionnaires.see((Questionnaire) m, packageInfo);
547        } else if (r instanceof ConceptMap) {
548          maps.see((ConceptMap) m, packageInfo);
549        } else if (r instanceof StructureMap) {
550          transforms.see((StructureMap) m, packageInfo);
551        } else if (r instanceof NamingSystem) {
552          systems.see((NamingSystem) m, packageInfo);
553          systemUrlMap = null;
554        } else if (r instanceof Requirements) {
555          requirements.see((Requirements) m, packageInfo);
556        } else if (r instanceof ActorDefinition) {
557          actors.see((ActorDefinition) m, packageInfo);
558          systemUrlMap = null;
559        }
560      }
561    }
562  }
563
564  public Map<String, NamingSystem> getNSUrlMap() {
565    if (systemUrlMap == null) {
566      systemUrlMap = new HashMap<>();
567      try {
568      List<NamingSystem> nsl = systems.getList();
569      for (NamingSystem ns : nsl) {
570        for (NamingSystemUniqueIdComponent uid : ns.getUniqueId()) {
571          if (uid.getType() == NamingSystemIdentifierType.URI && uid.hasValue()) {
572            systemUrlMap.put(uid.getValue(), ns) ;
573          }
574        }        
575      }
576      } catch (Exception e) {
577        if (!nsFailHasFailed) {
578          e.printStackTrace();
579          nsFailHasFailed  = true;
580        }
581      }
582    }
583    return systemUrlMap;
584  }
585
586  
587  public void fixOldSD(StructureDefinition sd) {
588    if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && sd.getType().equals("Extension") && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
589      sd.setSnapshot(null);
590    }
591    for (ElementDefinition ed : sd.getDifferential().getElement()) {
592      if (ed.getPath().equals("Extension.url") || ed.getPath().endsWith(".extension.url") ) {
593        ed.setMin(1);
594        if (ed.hasBase()) {
595          ed.getBase().setMin(1);
596        }
597      }
598      if ("extension".equals(ed.getSliceName())) {
599        ed.setSliceName(null);
600      }
601    }
602  }
603
604  /*
605   *  Compare business versions, returning "true" if the candidate newer version is in fact newer than the oldVersion
606   *  Comparison will work for strictly numeric versions as well as multi-level versions separated by ., -, _, : or space
607   *  Failing that, it will do unicode-based character ordering.
608   *  E.g. 1.5.3 < 1.14.3
609   *       2017-3-10 < 2017-12-7
610   *       A3 < T2
611   */
612  private boolean laterVersion(String newVersion, String oldVersion) {
613    // Compare business versions, retur
614    newVersion = newVersion.trim();
615    oldVersion = oldVersion.trim();
616    if (StringUtils.isNumeric(newVersion) && StringUtils.isNumeric(oldVersion)) {
617      return Double.parseDouble(newVersion) > Double.parseDouble(oldVersion);
618    } else if (hasDelimiter(newVersion, oldVersion, ".")) {
619      return laterDelimitedVersion(newVersion, oldVersion, "\\.");
620    } else if (hasDelimiter(newVersion, oldVersion, "-")) {
621      return laterDelimitedVersion(newVersion, oldVersion, "\\-");
622    } else if (hasDelimiter(newVersion, oldVersion, "_")) {
623      return laterDelimitedVersion(newVersion, oldVersion, "\\_");
624    } else if (hasDelimiter(newVersion, oldVersion, ":")) {
625      return laterDelimitedVersion(newVersion, oldVersion, "\\:");
626    } else if (hasDelimiter(newVersion, oldVersion, " ")) {
627      return laterDelimitedVersion(newVersion, oldVersion, "\\ ");
628    } else {
629      return newVersion.compareTo(oldVersion) > 0;
630    }
631  }
632  
633  /*
634   * Returns true if both strings include the delimiter and have the same number of occurrences of it
635   */
636  private boolean hasDelimiter(String s1, String s2, String delimiter) {
637    return s1.contains(delimiter) && s2.contains(delimiter) && s1.split(delimiter).length == s2.split(delimiter).length;
638  }
639
640  private boolean laterDelimitedVersion(String newVersion, String oldVersion, String delimiter) {
641    String[] newParts = newVersion.split(delimiter);
642    String[] oldParts = oldVersion.split(delimiter);
643    for (int i = 0; i < newParts.length; i++) {
644      if (!newParts[i].equals(oldParts[i])) {
645        return laterVersion(newParts[i], oldParts[i]);
646      }
647    }
648    // This should never happen
649    throw new Error(formatMessage(I18nConstants.DELIMITED_VERSIONS_HAVE_EXACT_MATCH_FOR_DELIMITER____VS_, delimiter, newParts, oldParts));
650  }
651  
652  protected <T extends CanonicalResource> void seeMetadataResource(T r, Map<String, T> map, List<T> list, boolean addId) throws FHIRException {
653//    if (addId)
654    //      map.put(r.getId(), r); // todo: why?
655    list.add(r);
656    if (r.hasUrl()) {
657      // first, this is the correct reosurce for this version (if it has a version)
658      if (r.hasVersion()) {
659        map.put(r.getUrl()+"|"+r.getVersion(), r);
660      }
661      // if we haven't get anything for this url, it's the correct version
662      if (!map.containsKey(r.getUrl())) {
663        map.put(r.getUrl(), r);
664      } else {
665        List<T> rl = new ArrayList<T>();
666        for (T t : list) {
667          if (t.getUrl().equals(r.getUrl()) && !rl.contains(t)) {
668            rl.add(t);
669          }
670        }
671        Collections.sort(rl, new MetadataResourceVersionComparator<T>(list));
672        map.put(r.getUrl(), rl.get(rl.size()-1));
673        T latest = null;
674        for (T t : rl) {
675          if (VersionUtilities.versionsCompatible(t.getVersion(), r.getVersion())) {
676            latest = t;
677          }
678        }
679        if (latest != null) { // might be null if it's not using semver
680          map.put(r.getUrl()+"|"+VersionUtilities.getMajMin(latest.getVersion()), rl.get(rl.size()-1));
681        }
682      }
683    }
684  }  
685
686  @Override
687  public CodeSystem fetchCodeSystem(String system, FhirPublication fhirVersion) {
688    return fetchCodeSystem(system);
689  }
690  
691  @Override
692  public CodeSystem fetchCodeSystem(String system) {
693    if (system == null) {
694      return null;
695    }
696    if (system.contains("|")) {
697      String s = system.substring(0, system.indexOf("|"));
698      String v = system.substring(system.indexOf("|")+1);
699      return fetchCodeSystem(s, v);
700    }
701    CodeSystem cs;
702    synchronized (lock) {
703      cs = codeSystems.get(system);
704    }
705    if (cs == null && locator != null) {
706      locator.findResource(this, system);
707      synchronized (lock) {
708        cs = codeSystems.get(system);
709      }
710    }
711    return cs;
712  } 
713
714
715  public CodeSystem fetchCodeSystem(String system, String version, FhirPublication fhirVersion) {
716    return fetchCodeSystem(system, version);
717  }
718  
719  public CodeSystem fetchCodeSystem(String system, String version) {
720    if (version == null) {
721      return fetchCodeSystem(system);
722    }
723    CodeSystem cs;
724    synchronized (lock) {
725      cs = codeSystems.get(system, version);
726    }
727    if (cs == null && locator != null) {
728      locator.findResource(this, system);
729      synchronized (lock) {
730        cs = codeSystems.get(system);
731      }
732    }
733    return cs;
734  } 
735  
736
737  public CodeSystem fetchSupplementedCodeSystem(String system, FhirPublication fhirVersion) {
738    return fetchSupplementedCodeSystem(system);  
739  }
740  
741  public CodeSystem fetchSupplementedCodeSystem(String system, String version, FhirPublication fhirVersion) {
742    return fetchSupplementedCodeSystem(system, version);
743  }
744  
745  @Override
746  public CodeSystem fetchSupplementedCodeSystem(String system) {
747    CodeSystem cs = fetchCodeSystem(system);
748    if (cs != null) {
749      List<CodeSystem> supplements = codeSystems.getSupplements(cs);
750      if (supplements.size() > 0) {
751        cs = CodeSystemUtilities.mergeSupplements(cs, supplements);
752      }
753    }
754    return cs;
755  }
756
757  @Override
758  public CodeSystem fetchSupplementedCodeSystem(String system, String version) {
759    CodeSystem cs = fetchCodeSystem(system, version);
760    if (cs != null) {
761      List<CodeSystem> supplements = codeSystems.getSupplements(cs);
762      if (supplements.size() > 0) {
763        cs = CodeSystemUtilities.mergeSupplements(cs, supplements);
764      }
765    }
766    return cs;
767  }
768
769
770  public boolean supportsSystem(String system, FhirPublication fhirVersion) throws TerminologyServiceException {
771    return supportsSystem(system);
772  }
773  
774  @Override
775  public boolean supportsSystem(String system) throws TerminologyServiceException {
776    synchronized (lock) {
777      if (codeSystems.has(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT) {
778        return true;
779      } else if (supportedCodeSystems.contains(system)) {
780        return true;
781      } else if (system.startsWith("http://example.org") || system.startsWith("http://acme.com") || system.startsWith("http://hl7.org/fhir/valueset-") || system.startsWith("urn:oid:")) {
782        return false;
783      } else {
784        if (noTerminologyServer) {
785          return false;
786        }
787        if (terminologyClientManager != null) {
788          try {
789            if (terminologyClientManager.supportsSystem(system)) {
790              supportedCodeSystems.add(system);
791            }
792          } catch (Exception e) {
793            if (canRunWithoutTerminology) {
794              noTerminologyServer = true;
795              logger.logMessage("==============!! Running without terminology server !! ==============");
796              if (terminologyClientManager.getMasterClient() != null) {
797                logger.logMessage("txServer = "+ terminologyClientManager.getMasterClient().getId());
798                logger.logMessage("Error = "+e.getMessage()+"");
799              }
800              logger.logMessage("=====================================================================");
801              return false;
802            } else {
803              e.printStackTrace();
804              throw new TerminologyServiceException(e);
805            }
806          }
807          if (supportedCodeSystems.contains(system)) {
808            return true;
809          }
810        }
811      }
812      return false;
813    }
814  }
815
816
817  public boolean isServerSideSystem(String url) {
818    boolean check = supportsSystem(url);
819    return check && supportedCodeSystems.contains(url);
820  }
821
822
823  protected void txLog(String msg) {
824    if (tlogging ) {
825        logger.logDebugMessage(LogCategory.TX, msg);
826    }
827  }
828
829  // --- expansion support ------------------------------------------------------------------------------------------------------------
830
831  public int getExpandCodesLimit() {
832    return expandCodesLimit;
833  }
834
835  public void setExpandCodesLimit(int expandCodesLimit) {
836    this.expandCodesLimit = expandCodesLimit;
837  }
838
839  @Override
840  public ValueSetExpansionOutcome expandVS(Resource src, ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heirarchical) throws FHIRException {
841    ValueSet vs = null;
842    vs = fetchResource(ValueSet.class, binding.getValueSet(), src);
843    if (vs == null) {
844      throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_VALUE_SET_, binding.getValueSet()));
845    }
846    return expandVS(vs, cacheOk, heirarchical);
847  }
848
849
850  @Override
851  public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean hierarchical, boolean noInactive) throws TerminologyServiceException {
852    ValueSet vs = new ValueSet();
853    vs.setStatus(PublicationStatus.ACTIVE);
854    vs.setCompose(new ValueSetComposeComponent());
855    vs.getCompose().setInactive(!noInactive);
856    vs.getCompose().getInclude().add(inc);
857    CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical);
858    ValueSetExpansionOutcome res;
859    res = txCache.getExpansion(cacheToken);
860    if (res != null) {
861      return res;
862    }
863    Set<String> systems = findRelevantSystems(vs);
864    TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, true);
865    if (tc == null) {
866      return new ValueSetExpansionOutcome("No server available", TerminologyServiceErrorClass.INTERNAL_ERROR, true);      
867    }
868    Parameters p = constructParameters(tc, vs, hierarchical);
869    for (ConceptSetComponent incl : vs.getCompose().getInclude()) {
870      codeSystemsUsed.add(incl.getSystem());
871    }
872    for (ConceptSetComponent incl : vs.getCompose().getExclude()) {
873      codeSystemsUsed.add(incl.getSystem());
874    }
875    
876    if (noTerminologyServer) {
877      return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, false);
878    }
879    p.addParameter("count", expandCodesLimit);
880    p.addParameter("offset", 0);
881    txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress());
882    if (addDependentResources(tc, p, vs)) {
883      p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId()));
884    }
885
886    try {
887      ValueSet result = tc.getClient().expandValueset(vs, p);
888      res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());  
889    } catch (Exception e) {
890      res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, true);
891      if (txLog != null) {
892        res.setTxLink(txLog.getLastId());
893      }
894    }
895    txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
896    return res;
897  }
898
899  @Override
900  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical) {
901    if (expParameters == null)
902      throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED));
903    Parameters p = expParameters.copy(); 
904    return expandVS(vs, cacheOk, heirarchical, false, p);
905  }
906
907  @Override
908  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk) {
909    if (expParameters == null)
910      throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED));
911    Parameters p = expParameters.copy(); 
912    return expandVS(vs, cacheOk, heirarchical, incompleteOk, p);
913  }
914
915  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean hierarchical, boolean incompleteOk, Parameters pIn)  {
916    return expandVS(vs, cacheOk, hierarchical, incompleteOk, pIn, false);
917  }
918  
919  public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean hierarchical, boolean incompleteOk, Parameters pIn, boolean noLimits)  {
920    if (pIn == null) {
921      throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS));
922    }
923    if (vs.hasUrl() && (vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-time-units") || vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-distance-units"))) {
924      return new ValueSetExpansionOutcome("This value set is not expanded correctly at this time (will be fixed in a future version)", TerminologyServiceErrorClass.VALUESET_UNSUPPORTED, false);
925    }
926    
927    Parameters p = pIn.copy();
928    p.setParameter("_limit",new IntegerType("10000"));
929    p.setParameter("_incomplete", new BooleanType("true"));
930    if (vs.hasExpansion()) {
931      return new ValueSetExpansionOutcome(vs.copy());
932    }
933    if (!vs.hasUrl()) {
934      throw new Error(formatMessage(I18nConstants.NO_VALUE_SET_IN_URL));
935    }
936    for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
937      if (inc.hasSystem()) {
938        codeSystemsUsed.add(inc.getSystem());
939      }
940    }
941    for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
942      if (inc.hasSystem()) {
943        codeSystemsUsed.add(inc.getSystem());
944      }
945    }
946
947    CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical);
948    ValueSetExpansionOutcome res;
949    if (cacheOk) {
950      res = txCache.getExpansion(cacheToken);
951      if (res != null) {
952        return res;
953      }
954    }
955
956    if (!noLimits) {
957      p.addParameter("count", expandCodesLimit);
958      p.addParameter("offset", 0);
959    }
960    p.setParameter("excludeNested", !hierarchical);
961    if (incompleteOk) {
962      p.setParameter("incomplete-ok", true);      
963    }
964
965    List<String> allErrors = new ArrayList<>();
966    
967    // ok, first we try to expand locally
968    ValueSetExpander vse = constructValueSetExpanderSimple(new ValidationOptions(vs.getFHIRPublicationVersion()));
969    res = null;
970    try {
971      res = vse.expand(vs, p);
972    } catch (Exception e) {
973      allErrors.addAll(vse.getAllErrors());
974      e.printStackTrace();
975      res = new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, e instanceof EFhirClientException);
976    }
977    allErrors.addAll(vse.getAllErrors());
978    if (res.getValueset() != null) {
979      if (!res.getValueset().hasUrl()) {
980        throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET));
981      }
982      txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT);
983      return res;
984    }
985    if (res.getErrorClass() == TerminologyServiceErrorClass.INTERNAL_ERROR || isNoTerminologyServer()) { // this class is created specifically to say: don't consult the server
986      return new ValueSetExpansionOutcome(res.getError(), res.getErrorClass(), false);
987    }
988
989    // if that failed, we try to expand on the server
990    if (noTerminologyServer) {
991      return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, allErrors, false);
992    }
993
994    p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId()));
995    Set<String> systems = findRelevantSystems(vs);
996    TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, true);
997    addDependentResources(tc, p, vs);
998
999    
1000    txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress());
1001    
1002    try {
1003      ValueSet result = tc.getClient().expandValueset(vs, p);
1004      if (result != null) {
1005        if (!result.hasUrl()) {
1006          result.setUrl(vs.getUrl());
1007        }
1008        if (!result.hasUrl()) {
1009          throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
1010        }
1011      }
1012      res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());  
1013    } catch (Exception e) {
1014      if (res != null && !res.isFromServer()) {
1015        res = new ValueSetExpansionOutcome(res.getError()+" (and "+e.getMessage()+")", res.getErrorClass(), false);
1016      } else {
1017        res = new ValueSetExpansionOutcome((e.getMessage() == null ? e.getClass().getName() : e.getMessage()), TerminologyServiceErrorClass.UNKNOWN, allErrors, true).setTxLink(txLog == null ? null : txLog.getLastId());
1018      }
1019    }
1020    txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
1021    return res;
1022  }
1023
1024//  private boolean hasTooCostlyExpansion(ValueSet valueset) {
1025//    return valueset != null && valueset.hasExpansion() && ToolingExtensions.hasExtension(valueset.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY);
1026//  }
1027  
1028  // --- validate code -------------------------------------------------------------------------------
1029  
1030  @Override
1031  public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display) {
1032    assert options != null;
1033    Coding c = new Coding(system, version, code, display);
1034    return validateCode(options, c, null);
1035  }
1036
1037  @Override
1038  public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display, ValueSet vs) {
1039    assert options != null;
1040    Coding c = new Coding(system, version, code, display);
1041    ValidationResult ret = validateCode(options, "$", c, vs);
1042    ret.trimPath("$");
1043    return ret;
1044  }
1045
1046  @Override
1047  public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) {
1048    assert options != null;
1049    Coding c = new Coding(null, code, null);
1050    return validateCode(options.withGuessSystem(), c, vs);
1051  }
1052
1053
1054  @Override
1055  public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) {
1056    if (options == null) {
1057      options = ValidationOptions.defaults();
1058    }
1059    // 1st pass: what is in the cache? 
1060    // 2nd pass: What can we do internally 
1061    // 3rd pass: hit the server
1062    for (CodingValidationRequest t : codes) {
1063      t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs == null ? t.getVsObj() : vs, expParameters) : null);
1064      if (t.getCoding().hasSystem()) {
1065        codeSystemsUsed.add(t.getCoding().getSystem());
1066      }
1067      if (txCache != null) { 
1068        t.setResult(txCache.getValidation(t.getCacheToken()));
1069      }
1070    }
1071    if (options.isUseClient()) {
1072      for (CodingValidationRequest t : codes) {
1073        if (!t.hasResult()) {
1074          try {
1075            ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs == null ? t.getVsObj() : vs);
1076            vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
1077            ValidationResult res = vsc.validateCode("Coding", t.getCoding());
1078            if (txCache != null) {
1079              txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
1080            }
1081            t.setResult(res);
1082          } catch (Exception e) {
1083          }
1084        }
1085      }      
1086    }  
1087
1088    for (CodingValidationRequest t : codes) {
1089      if (!t.hasResult()) {
1090        String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem();
1091        if (!options.isUseServer()) {
1092         t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null));
1093        } else if (unsupportedCodeSystems.contains(codeKey)) {
1094          t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null));      
1095        } else if (noTerminologyServer) {
1096          t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null));
1097        }
1098      }
1099    }
1100    
1101    if (expParameters == null)
1102      throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
1103    // for those that that failed, we try to validate on the server
1104    Bundle batch = new Bundle();
1105    batch.setType(BundleType.BATCH);
1106    Set<String> systems = findRelevantSystems(vs);
1107    for (CodingValidationRequest codingValidationRequest : codes) {
1108      if (!codingValidationRequest.hasResult()) {
1109        Parameters pIn = constructParameters(options, codingValidationRequest, vs == null ? codingValidationRequest.getVsObj() : vs);
1110        setTerminologyOptions(options, pIn);
1111        BundleEntryComponent be = batch.addEntry();
1112        be.setResource(pIn);
1113        be.getRequest().setMethod(HTTPVerb.POST);
1114        if (vs != null || codingValidationRequest.getVsObj() != null) {
1115          be.getRequest().setUrl("ValueSet/$validate-code");          
1116        } else {
1117          be.getRequest().setUrl("CodeSystem/$validate-code");
1118        }
1119        be.setUserData("source", codingValidationRequest);
1120        systems.add(codingValidationRequest.getCoding().getSystem());
1121        findRelevantSystems(systems, codingValidationRequest.getCoding());
1122      }
1123    }
1124    
1125    if (batch.getEntry().size() > 0) {
1126      TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
1127      Bundle resp = processBatch(tc, batch, systems);      
1128      for (int i = 0; i < batch.getEntry().size(); i++) {
1129        CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
1130        BundleEntryComponent r = resp.getEntry().get(i);
1131
1132        if (r.getResource() instanceof Parameters) {
1133          t.setResult(processValidationResult((Parameters) r.getResource(), vs != null ? vs.getUrl() : t.getVsObj() != null ? t.getVsObj().getUrl() : null, tc.getAddress()));
1134          if (txCache != null) {
1135            txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT);
1136          }
1137        } else {
1138          t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId()));          
1139        }
1140      }
1141    }    
1142  }
1143
1144  private Bundle processBatch(TerminologyClientContext tc, Bundle batch, Set<String> systems) {
1145    txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
1146    if (terminologyClientManager == null) {
1147      throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
1148    }
1149    if (txLog != null) {
1150      txLog.clearLastId();
1151    }
1152    Bundle resp = tc.getClient().validateBatch(batch);
1153    if (resp == null) {
1154      throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));          
1155    }
1156    return resp;
1157  }
1158  
1159  @Override
1160  public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl) {
1161    if (options == null) {
1162      options = ValidationOptions.defaults();
1163    }
1164    // 1st pass: what is in the cache? 
1165    // 2nd pass: What can we do internally 
1166    // 3rd pass: hit the server
1167    for (CodingValidationRequest t : codes) {
1168      t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vsUrl, expParameters) : null);
1169      if (t.getCoding().hasSystem()) {
1170        codeSystemsUsed.add(t.getCoding().getSystem());
1171      }
1172      if (txCache != null) { 
1173        t.setResult(txCache.getValidation(t.getCacheToken()));
1174      }
1175    }
1176    ValueSet vs = fetchResource(ValueSet.class, vsUrl);
1177    if (options.isUseClient()) {
1178      if (vs != null) {
1179        for (CodingValidationRequest t : codes) {
1180          if (!t.hasResult()) {
1181            try {
1182              ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
1183              vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
1184              ValidationResult res = vsc.validateCode("Coding", t.getCoding());
1185              if (txCache != null) {
1186                txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
1187              }
1188              t.setResult(res);
1189            } catch (Exception e) {
1190            }
1191          }
1192        }      
1193      }  
1194    }
1195
1196    for (CodingValidationRequest t : codes) {
1197      if (!t.hasResult()) {
1198        String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem();
1199        if (!options.isUseServer()) {
1200         t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null));
1201        } else if (unsupportedCodeSystems.contains(codeKey)) {
1202          t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null));      
1203        } else if (noTerminologyServer) {
1204          t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null));
1205        }
1206      }
1207    }
1208    
1209    if (expParameters == null)
1210      throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
1211    // for those that that failed, we try to validate on the server
1212    Bundle batch = new Bundle();
1213    batch.setType(BundleType.BATCH);
1214    Set<String> systems = vs != null ? findRelevantSystems(vs) : new HashSet<>();
1215    for (CodingValidationRequest codingValidationRequest : codes) {
1216      if (!codingValidationRequest.hasResult()) {
1217        Parameters pIn = constructParameters(options, codingValidationRequest, vsUrl);
1218        setTerminologyOptions(options, pIn);
1219        BundleEntryComponent be = batch.addEntry();
1220        be.setResource(pIn);
1221        be.getRequest().setMethod(HTTPVerb.POST);
1222        if (vsUrl != null) {
1223          be.getRequest().setUrl("ValueSet/$validate-code");
1224        } else {
1225          be.getRequest().setUrl("CodeSystem/$validate-code");
1226        }
1227        be.setUserData("source", codingValidationRequest);
1228        systems.add(codingValidationRequest.getCoding().getSystem());
1229      }
1230    }
1231    TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
1232    
1233    if (batch.getEntry().size() > 0) {
1234      Bundle resp = processBatch(tc, batch, systems);      
1235      for (int i = 0; i < batch.getEntry().size(); i++) {
1236        CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
1237        BundleEntryComponent r = resp.getEntry().get(i);
1238
1239        if (r.getResource() instanceof Parameters) {
1240          t.setResult(processValidationResult((Parameters) r.getResource(), vsUrl, tc.getAddress()));
1241          if (txCache != null) {
1242            txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT);
1243          }
1244        } else {
1245          t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId()));          
1246        }
1247      }
1248    }    
1249  }
1250  
1251  private String getResponseText(Resource resource) {
1252    if (resource instanceof OperationOutcome) {
1253      return OperationOutcomeRenderer.toString((OperationOutcome) resource);
1254    }
1255    return "Todo";
1256  }
1257
1258  @Override
1259  public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
1260    ValidationContextCarrier ctxt = new ValidationContextCarrier();
1261    return validateCode(options, "Coding", code, vs, ctxt);
1262  }
1263  
1264  public ValidationResult validateCode(ValidationOptions options, String path, Coding code, ValueSet vs) {
1265    ValidationContextCarrier ctxt = new ValidationContextCarrier();
1266    return validateCode(options, path, code, vs, ctxt);
1267  }
1268
1269  private final String getCodeKey(Coding code) {
1270    return code.hasVersion() ? code.getSystem()+"|"+code.getVersion() : code.getSystem();
1271  }
1272
1273  @Override
1274  public ValidationResult validateCode(final ValidationOptions optionsArg, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) {
1275    return validateCode(optionsArg, "Coding", code, vs, ctxt); 
1276  }
1277  
1278  public ValidationResult validateCode(final ValidationOptions optionsArg, String path, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) {
1279  
1280    ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults();
1281    
1282    if (code.hasSystem()) {
1283      codeSystemsUsed.add(code.getSystem());
1284    }
1285
1286    final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateValidationToken(options, code, vs, expParameters) : null;
1287    ValidationResult res = null;
1288    if (cachingAllowed && txCache != null) {
1289      res = txCache.getValidation(cacheToken);
1290    }
1291    if (res != null) {
1292      updateUnsupportedCodeSystems(res, code, getCodeKey(code));
1293      return res;
1294    }
1295
1296    List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
1297    Set<String> unknownSystems = new HashSet<>();
1298    
1299    String localError = null;
1300    String localWarning = null;
1301    TerminologyServiceErrorClass type = TerminologyServiceErrorClass.UNKNOWN;
1302    if (options.isUseClient()) {
1303      // ok, first we try to validate locally
1304      try {
1305        ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt);
1306        if (vsc.getOpContext() != null) {
1307          vsc.getOpContext().note("Validate "+code.toString()+" @ "+path+" against "+(vs == null ? "null" : vs.getVersionedUrl()));
1308        }
1309        vsc.setUnknownSystems(unknownSystems);
1310        vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
1311        if (!ValueSetUtilities.isServerSide(code.getSystem())) {
1312          res = vsc.validateCode(path, code.copy());
1313          if (txCache != null && cachingAllowed) {
1314            txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
1315          }
1316          return res;
1317        }
1318      } catch (VSCheckerException e) {
1319        if (e.isWarning()) {
1320          localWarning = e.getMessage();
1321        } else {  
1322          localError = e.getMessage();
1323        }
1324        if (e.getIssues() != null) {
1325          issues.addAll(e.getIssues());
1326        }
1327        type = e.getType();
1328      } catch (TerminologyServiceProtectionException e) {
1329        OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType());
1330        iss.getDetails().setText(e.getMessage());
1331        issues.add(iss);
1332        return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues);
1333      } catch (Exception e) {
1334//        e.printStackTrace();
1335        localError = e.getMessage();
1336      }
1337    }
1338    
1339    if (localError != null && !terminologyClientManager.hasClient()) {
1340      if (unknownSystems.size() > 0) {
1341        return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
1342      } else {
1343        return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
1344      }
1345    }
1346    if (localWarning != null && !terminologyClientManager.hasClient()) {
1347      return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);       
1348    }
1349    if (!options.isUseServer()) {
1350      if (localWarning != null) {
1351        return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);       
1352      } else {
1353        return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localError), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);
1354      }
1355    }
1356    String codeKey = getCodeKey(code);
1357    if (unsupportedCodeSystems.contains(codeKey)) {
1358      return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, code.getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues);      
1359    }
1360    
1361    // if that failed, we try to validate on the server
1362    if (noTerminologyServer) {
1363      return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, code.getCode(), code.getSystem()), TerminologyServiceErrorClass.NOSERVICE, issues);
1364    }
1365
1366    Set<String> systems = findRelevantSystems(code, vs);
1367    TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
1368    
1369    String csumm = cachingAllowed && txCache != null ? txCache.summary(code) : null;
1370    if (cachingAllowed && txCache != null) {
1371      txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress());
1372    } else {
1373      txLog("$validate "+csumm+" before cache exists on "+tc.getAddress());
1374    }
1375    try {
1376      Parameters pIn = constructParameters(options, code);
1377      res = validateOnServer(tc, vs, pIn, options);
1378    } catch (Exception e) {
1379      res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR);
1380    }
1381    if (!res.isOk() && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && (localError != null && !localError.equals(ValueSetValidator.NO_TRY_THE_SERVER))) {
1382      res = new ValidationResult(IssueSeverity.ERROR, localError, null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(type);
1383    } 
1384    if (!res.isOk() && localError != null) {
1385      res.setDiagnostics("Local Error: "+localError.trim()+". Server Error: "+res.getMessage());
1386    } else if (!res.isOk() && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && res.getUnknownSystems() != null && res.getUnknownSystems().contains(codeKey) && localWarning != null) {
1387      // we had some problem evaluating locally, but the server doesn't know the code system, so we'll just go with the local error
1388      res = new ValidationResult(IssueSeverity.WARNING, localWarning, null);
1389      res.setDiagnostics("Local Warning: "+localWarning.trim()+". Server Error: "+res.getMessage());
1390      return res;
1391    }
1392    updateUnsupportedCodeSystems(res, code, codeKey);
1393    if (cachingAllowed && txCache != null) { // we never cache unsupported code systems - we always keep trying (but only once per run)
1394      txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
1395    }
1396    return res;
1397  }
1398
1399
1400  /**
1401   * ask the terminology system whether parent subsumes child. 
1402   * 
1403   * @return true if it does, false if it doesn't, and null if it's not know whether it does
1404   */
1405  public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) {
1406    ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults();
1407
1408    if (parent.hasSystem()) {
1409      codeSystemsUsed.add(parent.getSystem());
1410    } else {
1411      return null;
1412    }
1413    if (child.hasSystem()) {
1414      codeSystemsUsed.add(child.getSystem());
1415    } else {
1416      return null;
1417    }
1418
1419    final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateSubsumesToken(options, parent, child, expParameters) : null;
1420    if (cachingAllowed && txCache != null) {
1421      Boolean res = txCache.getSubsumes(cacheToken);
1422      if (res != null) {
1423        return res;
1424      }
1425    }
1426    
1427    if (options.isUseClient() && parent.getSystem().equals(child.getSystem())) {
1428      CodeSystem cs = fetchCodeSystem(parent.getSystem());
1429      if (cs != null) {
1430        Boolean b = CodeSystemUtilities.subsumes(cs, parent.getCode(), child.getCode());
1431        if (txCache != null && cachingAllowed) {
1432          txCache.cacheSubsumes(cacheToken, b, true);
1433        }
1434        return b;
1435      }
1436    }
1437
1438    if (!terminologyClientManager.hasClient() || !options.isUseServer() || unsupportedCodeSystems.contains(parent.getSystem()) || unsupportedCodeSystems.contains(child.getSystem()) || noTerminologyServer) {
1439      return null;      
1440    }
1441
1442    Set<String> systems = new HashSet<>();
1443    systems.add(parent.getSystem());
1444    systems.add(child.getSystem());
1445    TerminologyClientContext tc = terminologyClientManager.chooseServer(null, systems, false);
1446    
1447    txLog("$subsumes "+parent.toString()+" > "+child.toString()+" on "+tc.getAddress());
1448
1449    try {
1450      Parameters pIn =  new Parameters();
1451      pIn.addParameter().setName("codingA").setValue(parent);
1452      pIn.addParameter().setName("codingB").setValue(child);
1453      if (txLog != null) {
1454        txLog.clearLastId();
1455      }
1456      Parameters pOut = tc.getClient().subsumes(pIn);
1457      return processSubsumesResult(pOut, tc.getClient().getAddress());
1458    } catch (Exception e) {
1459      // e.printStackTrace();
1460    }
1461    return null;
1462  }
1463
1464
1465  public Boolean processSubsumesResult(Parameters pOut, String server) {
1466    for (ParametersParameterComponent p : pOut.getParameter()) {
1467      if (p.hasValue()) {
1468        if (p.getName().equals("outcome")) {
1469          return Utilities.existsInList(p.getValue().primitiveValue(), "equivalent", "subsumes");
1470        }
1471      }
1472    }
1473    return null;
1474  }
1475
1476  protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
1477    return new ValueSetExpander(this, new TerminologyOperationContext(this, options, "expansion")).setDebug(logger.isDebugLogging());
1478  }
1479
1480  protected ValueSetValidator constructValueSetCheckerSimple(ValidationOptions options,  ValueSet vs,  ValidationContextCarrier ctxt) {
1481    return new ValueSetValidator(this, new TerminologyOperationContext(this, options, "validation"), options, vs, ctxt, expParameters, terminologyClientManager);
1482  }
1483
1484  protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options,  ValueSet vs) {
1485    return new ValueSetValidator(this, new TerminologyOperationContext(this, options, "validation"), options, vs, expParameters, terminologyClientManager);
1486  }
1487
1488  protected Parameters constructParameters(TerminologyClientContext tcd, ValueSet vs, boolean hierarchical) {
1489    Parameters p = expParameters.copy();
1490    p.setParameter("includeDefinition", false);
1491    p.setParameter("excludeNested", !hierarchical);
1492
1493    addDependentResources(tcd, p, vs);
1494    p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId()));
1495    return p;
1496  }
1497
1498  protected Parameters constructParameters(ValidationOptions options, Coding coding) {
1499    Parameters pIn = new Parameters();
1500    if (options.isGuessSystem()) {
1501      pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
1502      pIn.addParameter().setName("code").setValue(coding.getCodeElement());
1503    } else {
1504      pIn.addParameter().setName("coding").setValue(coding);
1505    }
1506    setTerminologyOptions(options, pIn);
1507    return pIn;
1508  }
1509
1510  protected Parameters constructParameters(ValidationOptions options, CodeableConcept codeableConcept) {
1511    Parameters pIn = new Parameters();
1512    pIn.addParameter().setName("codeableConcept").setValue(codeableConcept);
1513    setTerminologyOptions(options, pIn);
1514    return pIn;
1515  }
1516
1517  protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, ValueSet valueSet) {
1518    Parameters pIn = new Parameters();
1519    if (options.isGuessSystem()) {
1520      pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
1521      pIn.addParameter().setName("code").setValue(codingValidationRequest.getCoding().getCodeElement());
1522    } else {      
1523      pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding());
1524    }
1525    if (valueSet != null) {
1526      pIn.addParameter().setName("valueSet").setResource(valueSet);
1527    }
1528    
1529    pIn.addParameters(expParameters);
1530    return pIn;
1531  }
1532
1533  protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, String vsUrl) {
1534    Parameters pIn = new Parameters();
1535    if (options.isGuessSystem()) {
1536      pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
1537      pIn.addParameter().setName("code").setValue(codingValidationRequest.getCoding().getCodeElement());
1538    } else {
1539      pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding());
1540    }
1541    if (vsUrl != null) {
1542      pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl));
1543    }
1544    pIn.addParameters(expParameters);
1545    return pIn;
1546  }
1547
1548  private void updateUnsupportedCodeSystems(ValidationResult res, Coding code, String codeKey) {
1549    if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && !code.hasVersion() && fetchCodeSystem(codeKey) == null) {
1550      unsupportedCodeSystems.add(codeKey);
1551    }
1552  }
1553
1554  private void setTerminologyOptions(ValidationOptions options, Parameters pIn) {
1555    if (options.hasLanguages()) {
1556      pIn.addParameter("displayLanguage", options.getLanguages().toString());
1557    }
1558    if (options.isMembershipOnly()) {
1559      pIn.addParameter("valueset-membership-only", true);
1560    }
1561    if (options.isDisplayWarningMode()) {
1562      pIn.addParameter("lenient-display-validation", true);
1563    }
1564    if (options.isVersionFlexible()) {
1565      pIn.addParameter("default-to-latest-version", true);     
1566    }
1567  }
1568
1569  @Override
1570  public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) {
1571    CacheToken cacheToken = txCache.generateValidationToken(options, code, vs, expParameters);
1572    ValidationResult res = null;
1573    if (cachingAllowed) {
1574      res = txCache.getValidation(cacheToken);
1575      if (res != null) {
1576        return res;
1577      }
1578    }
1579    for (Coding c : code.getCoding()) {
1580      if (c.hasSystem()) {
1581        codeSystemsUsed.add(c.getSystem());
1582      }
1583    }
1584    Set<String> unknownSystems = new HashSet<>();
1585
1586    List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
1587    
1588    String localError = null;
1589    String localWarning = null;
1590    
1591    if (options.isUseClient()) {
1592      // ok, first we try to validate locally
1593      try {
1594        ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
1595        vsc.setUnknownSystems(unknownSystems);
1596        vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
1597        res = vsc.validateCode("CodeableConcept", code);
1598        if (cachingAllowed) {
1599          txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
1600        }
1601        return res;
1602      } catch (VSCheckerException e) {
1603        if (e.isWarning()) {
1604          localWarning = e.getMessage();
1605        } else {  
1606          localError = e.getMessage();
1607        }
1608        if (e.getIssues() != null) {
1609          issues.addAll(e.getIssues());
1610        }
1611      } catch (TerminologyServiceProtectionException e) {
1612        OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType());
1613        iss.getDetails().setText(e.getMessage());
1614        issues.add(iss);
1615        return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues);
1616      } catch (Exception e) {
1617//        e.printStackTrace();
1618        localError = e.getMessage();
1619      }
1620    }
1621
1622    if (localError != null && !terminologyClientManager.hasClient()) {
1623      if (unknownSystems.size() > 0) {
1624        return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
1625      } else {
1626        return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
1627      }
1628    }
1629    if (localWarning != null && !terminologyClientManager.hasClient()) {
1630      return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);       
1631    }
1632    
1633    if (!options.isUseServer()) {
1634      return new ValidationResult(IssueSeverity.WARNING, "Unable to validate code without using server", TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null);      
1635    }
1636    
1637    // if that failed, we try to validate on the server
1638    if (noTerminologyServer) {
1639      return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services", TerminologyServiceErrorClass.NOSERVICE, null);
1640    }
1641    Set<String> systems = findRelevantSystems(code, vs);
1642    TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
1643
1644    txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs)+" on "+tc.getAddress());
1645    try {
1646      Parameters pIn = constructParameters(options, code);
1647      res = validateOnServer(tc, vs, pIn, options);
1648    } catch (Exception e) {
1649      issues.clear();
1650      OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXCEPTION);
1651      iss.getDetails().setText(e.getMessage());
1652      issues.add(iss);
1653      res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), issues).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR);
1654    }
1655    if (cachingAllowed) {
1656      txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
1657    }
1658    return res;
1659  }
1660
1661  private Set<String> findRelevantSystems(ValueSet vs) {
1662    Set<String> set = new HashSet<>();
1663    if (vs != null) {
1664      findRelevantSystems(set, vs);
1665    }
1666    return set;
1667  }
1668
1669  private Set<String> findRelevantSystems(CodeableConcept code, ValueSet vs) {
1670    Set<String> set = new HashSet<>();
1671    if (vs != null) {
1672      findRelevantSystems(set, vs);
1673    }
1674    for (Coding c : code.getCoding()) {      
1675      findRelevantSystems(set, c);
1676    }
1677    return set;
1678  }
1679
1680  private Set<String> findRelevantSystems(Coding code, ValueSet vs) {
1681    Set<String> set = new HashSet<>();
1682    if (vs != null) {
1683      findRelevantSystems(set, vs);
1684    }
1685    if (code != null) {      
1686      findRelevantSystems(set, code);
1687    }
1688    return set;
1689  }
1690
1691  private void findRelevantSystems(Set<String> set, ValueSet vs) {
1692    for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
1693      findRelevantSystems(set, inc);
1694    }
1695    for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
1696      findRelevantSystems(set, inc);
1697    }    
1698  }
1699
1700  private void findRelevantSystems(Set<String> set, ConceptSetComponent inc) {
1701    if (inc.hasSystem()) {
1702      if (inc.hasVersion()) {
1703        set.add(inc.getSystem()+"|"+inc.getVersion());
1704      } else {
1705        set.add(inc.getSystem());
1706      }
1707    }
1708    for (CanonicalType u : inc.getValueSet()) {
1709      ValueSet vs = fetchResource(ValueSet.class, u.getValue());
1710      if (vs != null) {
1711        findRelevantSystems(set, vs);
1712      } else {
1713        set.add(TerminologyClientManager.UNRESOLVED_VALUESET);
1714      }
1715    }
1716  }
1717
1718  private void findRelevantSystems(Set<String> set, Coding c) {
1719    if (c.hasSystem()) {
1720      if (c.hasVersion()) {
1721        set.add(c.getSystem()+"|"+c.getVersion());
1722      } else {
1723        set.add(c.getSystem());
1724      }
1725    }    
1726  }
1727
1728  protected ValidationResult validateOnServer(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException {
1729
1730    if (vs != null) {
1731      for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
1732        codeSystemsUsed.add(inc.getSystem());
1733      }
1734      for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
1735        codeSystemsUsed.add(inc.getSystem());
1736      }
1737    }
1738
1739    addServerValidationParameters(tc, vs, pin, options);
1740
1741    if (txLog != null) {
1742      txLog.clearLastId();
1743    }
1744    if (tc == null) {
1745      throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
1746    }
1747    Parameters pOut;
1748    if (vs == null) {
1749      pOut = tc.getClient().validateCS(pin);
1750    } else {
1751      pOut = tc.getClient().validateVS(pin);
1752    }
1753    return processValidationResult(pOut, vs == null ? null : vs.getUrl(), tc.getClient().getAddress());
1754  }
1755
1756  protected void addServerValidationParameters(TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options) {
1757    boolean cache = false;
1758    if (vs != null) {
1759      if (terminologyClientContext != null && terminologyClientContext.isTxCaching() && terminologyClientContext.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) {
1760        pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
1761        if (vs.hasVersion()) {
1762          pin.addParameter().setName("valueSetVersion").setValue(new StringType(vs.getVersion()));            
1763        }
1764      } else if (options.getVsAsUrl()){
1765        pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
1766      } else {
1767        if (vs.hasCompose() && vs.hasExpansion()) {
1768          vs = vs.copy();
1769          vs.setExpansion(null);
1770        }
1771        pin.addParameter().setName("valueSet").setResource(vs);
1772        if (vs.getUrl() != null) {
1773          terminologyClientContext.getCached().add(vs.getUrl()+"|"+ vs.getVersion());
1774        }
1775      }
1776      cache = true;
1777      addDependentResources(terminologyClientContext, pin, vs);
1778    }
1779    pin.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId()));
1780    for (ParametersParameterComponent pp : pin.getParameter()) {
1781      if (pp.getName().equals("profile")) {
1782        throw new Error(formatMessage(I18nConstants.CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT));
1783      }
1784    }
1785    if (expParameters == null) {
1786      throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
1787    }
1788    pin.addParameters(expParameters);
1789
1790    if (options.isDisplayWarningMode()) {
1791      pin.addParameter("mode","lenient-display-validation");
1792    }
1793    pin.addParameter("diagnostics", true);
1794  }
1795
1796  private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ValueSet vs) {
1797    boolean cache = false;
1798    for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
1799      cache = addDependentResources(tc, pin, inc, vs) || cache;
1800    }
1801    for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
1802      cache = addDependentResources(tc, pin, inc, vs) || cache;
1803    }
1804    return cache;
1805  }
1806
1807  private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ConceptSetComponent inc, Resource src) {
1808    boolean cache = false;
1809    for (CanonicalType c : inc.getValueSet()) {
1810      ValueSet vs = fetchResource(ValueSet.class, c.getValue(), src);
1811      if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) {
1812        cache = checkAddToParams(tc, pin, vs) || cache;
1813        addDependentResources(tc, pin, vs);
1814        for (Extension ext : vs.getExtensionsByUrl(ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED)) {
1815          if (ext.hasValueCanonicalType()) {
1816            String url = ext.getValueCanonicalType().asStringValue();
1817            CodeSystem supp = fetchResource(CodeSystem.class, url);
1818            if (supp != null) {
1819              cache = checkAddToParams(tc, pin, supp) || cache;            
1820            }
1821          }
1822        }
1823      }
1824    }
1825    CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src);
1826    if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) {
1827      cache = checkAddToParams(tc, pin, cs) || cache;
1828    }
1829    for (CodeSystem supp : codeSystems.getSupplements(cs)) {
1830      //if (supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem())) {
1831      assert supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem());
1832        cache = checkAddToParams(tc, pin, supp) || cache;
1833      //}
1834    }
1835    return cache;
1836  }
1837
1838  private boolean checkAddToParams(TerminologyClientContext tc, Parameters pin, CanonicalResource cr) {
1839    boolean cache = false;
1840    boolean addToParams = false;
1841    if (tc.usingCache()) {
1842      if (!tc.alreadyCached(cr)) {
1843        tc.addToCache(cr);
1844        if (logger.isDebugLogging()) {
1845          logger.logMessage("add to cache: "+cr.getVUrl());
1846        }
1847        addToParams = true;
1848        cache = true;
1849      } else {
1850        if (logger.isDebugLogging()) {
1851          logger.logMessage("already cached: "+cr.getVUrl());
1852        }
1853      }
1854    } else {
1855      addToParams = true;
1856    }
1857    if (addToParams) {
1858      pin.addParameter().setName("tx-resource").setResource(cr);
1859    }
1860    return cache;
1861  }
1862
1863  private boolean hasCanonicalResource(Parameters pin, String name, String vUrl) {
1864    for (ParametersParameterComponent p : pin.getParameter()) {
1865      if (name.equals(p.getName()) && p.hasResource() &&
1866          p.getResource() instanceof CanonicalResource && vUrl.equals(((CanonicalResource) p.getResource()).getVUrl())) {
1867        return true;
1868      }
1869    }
1870    return false;
1871  }
1872
1873  public ValidationResult processValidationResult(Parameters pOut, String vs, String server) {
1874    boolean ok = false;
1875    String message = "No Message returned";
1876    String display = null;
1877    String system = null;
1878    String code = null;
1879    String version = null;
1880    boolean inactive = false;
1881    String status = null;
1882    List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
1883    Set<String> unknownSystems = new HashSet<>();
1884
1885    TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN;
1886    for (ParametersParameterComponent p : pOut.getParameter()) {
1887      if (p.hasValue()) {
1888        if (p.getName().equals("result")) {
1889          ok = ((BooleanType) p.getValue()).getValue().booleanValue();
1890        } else if (p.getName().equals("message")) {
1891          message = p.getValue().primitiveValue();
1892        } else if (p.getName().equals("display")) {
1893          display = p.getValue().primitiveValue();
1894        } else if (p.getName().equals("system")) {
1895          system = ((PrimitiveType<?>) p.getValue()).asStringValue();
1896        } else if (p.getName().equals("version")) {
1897          version = ((PrimitiveType<?>) p.getValue()).asStringValue();
1898        } else if (p.getName().equals("code")) {
1899          code = ((PrimitiveType<?>) p.getValue()).asStringValue();
1900        } else if (p.getName().equals("inactive")) {
1901          inactive = "true".equals(((PrimitiveType<?>) p.getValue()).asStringValue());
1902        } else if (p.getName().equals("status")) {
1903          status = ((PrimitiveType<?>) p.getValue()).asStringValue();
1904        } else if (p.getName().equals("x-caused-by-unknown-system")) {
1905          String unkSystem = ((PrimitiveType<?>) p.getValue()).asStringValue();
1906          if (unkSystem != null && unkSystem.contains("|")) {
1907            err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED_VERSION; 
1908            system = unkSystem.substring(0, unkSystem.indexOf("|"));
1909            version = unkSystem.substring(unkSystem.indexOf("|")+1);
1910          } else {
1911            err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED;            
1912            unknownSystems.add(unkSystem);      
1913          }
1914        } else if (p.getName().equals("x-unknown-system")) {
1915          unknownSystems.add(((PrimitiveType<?>) p.getValue()).asStringValue());      
1916        } else if (p.getName().equals("warning-withdrawn")) {
1917          String msg = ((PrimitiveType<?>) p.getValue()).asStringValue();
1918          OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
1919          iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_WITHDRAWN : I18nConstants.MSG_WITHDRAWN_SRC, msg, vs, impliedType(msg)));              
1920          issues.add(iss);
1921        } else if (p.getName().equals("warning-deprecated")) {
1922          String msg = ((PrimitiveType<?>) p.getValue()).asStringValue();
1923          OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
1924          iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DEPRECATED : I18nConstants.MSG_DEPRECATED_SRC, msg, vs, impliedType(msg)));              
1925          issues.add(iss);
1926        } else if (p.getName().equals("warning-retired")) {
1927          String msg = ((PrimitiveType<?>) p.getValue()).asStringValue();
1928          OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
1929          iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_RETIRED : I18nConstants.MSG_RETIRED_SRC, msg, vs, impliedType(msg)));              
1930          issues.add(iss);
1931        } else if (p.getName().equals("warning-experimental")) {
1932          String msg = ((PrimitiveType<?>) p.getValue()).asStringValue();
1933          OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
1934          iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_EXPERIMENTAL : I18nConstants.MSG_EXPERIMENTAL_SRC, msg, vs, impliedType(msg)));              
1935          issues.add(iss);
1936        } else if (p.getName().equals("warning-draft")) {
1937          String msg = ((PrimitiveType<?>) p.getValue()).asStringValue();
1938          OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
1939          iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DRAFT : I18nConstants.MSG_DRAFT_SRC, msg, vs, impliedType(msg)));              
1940          issues.add(iss);
1941        } else if (p.getName().equals("cause")) {
1942          try {
1943            IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue());
1944            if (it == IssueType.UNKNOWN) {
1945              err = TerminologyServiceErrorClass.UNKNOWN;
1946            } else if (it == IssueType.NOTFOUND) {
1947              err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED;
1948            } else if (it == IssueType.NOTSUPPORTED) {
1949              err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED;
1950            } else {
1951              err = null;
1952            }
1953          } catch (FHIRException e) {
1954          }
1955        }
1956      } else if (p.hasResource()) {
1957        if (p.getName().equals("issues")) {
1958          OperationOutcome oo = (OperationOutcome) p.getResource();
1959          for (OperationOutcomeIssueComponent iss : oo.getIssue()) {
1960            iss.addExtension(ToolingExtensions.EXT_ISSUE_SERVER, new UrlType(server));
1961            issues.add(iss);
1962          }
1963        } else {
1964          // nothing?
1965        }
1966      }
1967    }
1968    ValidationResult res = null;
1969    if (!ok) {
1970      res = new ValidationResult(IssueSeverity.ERROR, message, err, null).setTxLink(txLog.getLastId());
1971      if (code != null) {
1972        res.setDefinition(new ConceptDefinitionComponent().setDisplay(display).setCode(code));
1973        res.setDisplay(display);
1974      }
1975      if (system != null) {
1976        res.setSystem(system);
1977      }
1978      if (version != null) {
1979        res.setVersion(version);
1980      }
1981    } else if (message != null && !message.equals("No Message returned")) { 
1982      res = new ValidationResult(IssueSeverity.WARNING, message, system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog.getLastId());
1983    } else if (display != null) {
1984      res = new ValidationResult(system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display).setTxLink(txLog.getLastId());
1985    } else {
1986      res = new ValidationResult(system, version, new ConceptDefinitionComponent().setCode(code), null).setTxLink(txLog.getLastId());
1987    }
1988    res.setIssues(issues);
1989    res.setStatus(inactive, status);
1990    res.setUnknownSystems(unknownSystems);
1991    res.setServer(server);
1992    return res;
1993  }
1994
1995  // --------------------------------------------------------------------------------------------------------------------------------------------------------
1996  
1997  private Object impliedType(String msg) {
1998    if (msg.contains("/CodeSystem")) {
1999      return "CodeSystem";
2000    }
2001    if (msg.contains("/ValueSet")) {
2002      return "ValueSet";
2003    }
2004    return "item";
2005  }
2006
2007  public void initTxCache(String cachePath) throws FileNotFoundException, FHIRException, IOException {
2008    if (cachePath != null) {
2009      txCache = new TerminologyCache(lock, cachePath);
2010      initTxCache(txCache);
2011    }
2012  }
2013  
2014  public void initTxCache(TerminologyCache cache) {
2015    txCache = cache;
2016    terminologyClientManager.setCache(txCache);
2017  }
2018
2019  public void clearTSCache(String url) throws Exception {
2020    txCache.removeCS(url);
2021  }
2022
2023  public boolean isCanRunWithoutTerminology() {
2024    return canRunWithoutTerminology;
2025  }
2026
2027  public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) {
2028    this.canRunWithoutTerminology = canRunWithoutTerminology;
2029  }
2030
2031  public void setLogger(@Nonnull org.hl7.fhir.r5.context.ILoggingService logger) {
2032    this.logger = logger;
2033  }
2034
2035  public Parameters getExpansionParameters() {
2036    return expParameters;
2037  }
2038
2039  public void setExpansionParameters(Parameters expParameters) {
2040    this.expParameters = expParameters;
2041    this.terminologyClientManager.setExpansionParameters(expParameters);
2042  }
2043
2044  @Override
2045  public boolean isNoTerminologyServer() {
2046    return noTerminologyServer || !terminologyClientManager.hasClient();
2047  }
2048
2049  public void setNoTerminologyServer(boolean noTerminologyServer) {
2050    this.noTerminologyServer = noTerminologyServer;
2051  }
2052
2053  public String getName() {
2054    return name;
2055  }
2056
2057  public void setName(String name) {
2058    this.name = name;
2059  }
2060
2061
2062  public List<String> getResourceNames(FhirPublication fhirVersion) {
2063    return getResourceNames();    
2064  }
2065  
2066  public Set<String> getResourceNamesAsSet(FhirPublication fhirVersion) {
2067    return getResourceNamesAsSet();
2068  }
2069  
2070  @Override
2071  public Set<String> getResourceNamesAsSet() {
2072    Set<String> res = new HashSet<String>();
2073    res.addAll(getResourceNames());
2074    return res;
2075  }
2076
2077  public boolean isAllowLoadingDuplicates() {
2078    return allowLoadingDuplicates;
2079  }
2080
2081  public void setAllowLoadingDuplicates(boolean allowLoadingDuplicates) {
2082    this.allowLoadingDuplicates = allowLoadingDuplicates;
2083  }
2084
2085  @Override
2086  public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException {
2087    return fetchResourceWithException(class_, uri, null);
2088  }
2089  
2090  public <T extends Resource> T fetchResourceWithException(String cls, String uri) throws FHIRException {
2091    return fetchResourceWithExceptionByVersion(cls, uri, null, null);
2092  }
2093  
2094  public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, Resource sourceForReference) throws FHIRException {
2095    return fetchResourceWithExceptionByVersion(class_, uri, null, sourceForReference);
2096  }
2097  
2098  @SuppressWarnings("unchecked")
2099  public <T extends Resource> T fetchResourceWithExceptionByVersion(Class<T> class_, String uri, String version, Resource sourceForReference) throws FHIRException {
2100    if (uri == null) {
2101      return null;
2102    }
2103    if (uri.startsWith("#")) {
2104      if (sourceForReference != null && sourceForReference instanceof DomainResource) {
2105        for (Resource r : ((DomainResource) sourceForReference).getContained()) {
2106          if (r.getClass() == class_ &&( "#"+r.getIdBase()).equals(uri)) {
2107            if (r instanceof CanonicalResource) {
2108              CanonicalResource cr = (CanonicalResource) r;
2109              if (!cr.hasUrl()) {
2110                cr.setUrl(Utilities.makeUuidUrn());
2111              }              
2112            }
2113            return (T) r;
2114          }
2115        }
2116      }
2117      return null;
2118    }
2119    
2120    if (QA_CHECK_REFERENCE_SOURCE) {
2121      // it can be tricky to trace the source of a reference correctly. The code isn't water tight,
2122      // particularly around snapshot generation. Enable this code to check that the references are 
2123      // correct (but it's slow)
2124      if (sourceForReference != null && uri.contains("ValueSet")) {
2125        if (!ResourceUtilities.hasURL(uri, sourceForReference)) {
2126          System.out.print("Claimed source doesn't have url in it: "+sourceForReference.fhirType()+"/"+sourceForReference.getIdPart()+" -> "+uri);
2127          System.out.println();
2128        }
2129      }
2130    }
2131   
2132    List<String> pvlist = new ArrayList<>();
2133    if (sourceForReference != null && sourceForReference.getSourcePackage() != null) {
2134      populatePVList(pvlist, sourceForReference.getSourcePackage());
2135    }
2136    
2137    if (class_ == StructureDefinition.class) {
2138      uri = ProfileUtilities.sdNs(uri, null);
2139    }
2140    synchronized (lock) {
2141
2142      if (version == null) {
2143        if (uri.contains("|")) {
2144          version = uri.substring(uri.lastIndexOf("|")+1);
2145          uri = uri.substring(0, uri.lastIndexOf("|"));
2146        }
2147      } else {
2148        assert !uri.contains("|");
2149      }
2150      if (uri.contains("#")) {
2151        uri = uri.substring(0, uri.indexOf("#"));
2152      } 
2153      if (class_ == Resource.class || class_ == null) {
2154        if (structures.has(uri)) {
2155          return (T) structures.get(uri, version, pvlist);
2156        }        
2157        if (guides.has(uri)) {
2158          return (T) guides.get(uri, version, pvlist);
2159        } 
2160        if (capstmts.has(uri)) {
2161          return (T) capstmts.get(uri, version, pvlist);
2162        } 
2163        if (measures.has(uri)) {
2164          return (T) measures.get(uri, version, pvlist);
2165        } 
2166        if (libraries.has(uri)) {
2167          return (T) libraries.get(uri, version, pvlist);
2168        } 
2169        if (valueSets.has(uri)) {
2170          return (T) valueSets.get(uri, version, pvlist);
2171        } 
2172        if (codeSystems.has(uri)) {
2173          return (T) codeSystems.get(uri, version, pvlist);
2174        } 
2175        if (systems.has(uri)) {
2176          return (T) systems.get(uri, version, pvlist);
2177        } 
2178        if (operations.has(uri)) {
2179          return (T) operations.get(uri, version, pvlist);
2180        } 
2181        if (searchParameters.has(uri)) {
2182          return (T) searchParameters.get(uri, version, pvlist);
2183        } 
2184        if (plans.has(uri)) {
2185          return (T) plans.get(uri, version, pvlist);
2186        } 
2187        if (maps.has(uri)) {
2188          return (T) maps.get(uri, version, pvlist);
2189        } 
2190        if (transforms.has(uri)) {
2191          return (T) transforms.get(uri, version, pvlist);
2192        } 
2193        if (actors.has(uri)) {
2194          return (T) transforms.get(uri, version, pvlist);
2195        } 
2196        if (requirements.has(uri)) {
2197          return (T) transforms.get(uri, version, pvlist);
2198        } 
2199        if (questionnaires.has(uri)) {
2200          return (T) questionnaires.get(uri, version, pvlist);
2201        } 
2202
2203        for (Map<String, ResourceProxy> rt : allResourcesById.values()) {
2204          for (ResourceProxy r : rt.values()) {
2205            if (uri.equals(r.getUrl())) {
2206              if (version == null || version == r.getResource().getMeta().getVersionId()) {
2207                return (T) r.getResource();
2208              }
2209            }
2210          }            
2211        }
2212        if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
2213          return null;
2214        }
2215
2216        // it might be a special URL.
2217//        if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
2218//          Resource res = null; // findTxValueSet(uri);
2219//          if (res != null) {
2220//            return (T) res;
2221//          }
2222//        }
2223        return null;      
2224      } else if (class_ == ImplementationGuide.class) {
2225        return (T) guides.get(uri, version, pvlist);
2226      } else if (class_ == CapabilityStatement.class) {
2227        return (T) capstmts.get(uri, version, pvlist);
2228      } else if (class_ == Measure.class) {
2229        return (T) measures.get(uri, version, pvlist);
2230      } else if (class_ == Library.class) {
2231        return (T) libraries.get(uri, version, pvlist);
2232      } else if (class_ == StructureDefinition.class) {
2233        return (T) structures.get(uri, version, pvlist);
2234      } else if (class_ == StructureMap.class) {
2235        return (T) transforms.get(uri, version, pvlist);
2236      } else if (class_ == NamingSystem.class) {
2237        return (T) systems.get(uri, version, pvlist);
2238      } else if (class_ == ValueSet.class) {
2239        return (T) valueSets.get(uri, version, pvlist);
2240      } else if (class_ == CodeSystem.class) {
2241        return (T) codeSystems.get(uri, version, pvlist);
2242      } else if (class_ == ConceptMap.class) {
2243        return (T) maps.get(uri, version, pvlist);
2244      } else if (class_ == ActorDefinition.class) {
2245        return (T) actors.get(uri, version, pvlist);
2246      } else if (class_ == Requirements.class) {
2247        return (T) requirements.get(uri, version, pvlist);
2248      } else if (class_ == PlanDefinition.class) {
2249        return (T) plans.get(uri, version, pvlist);
2250      } else if (class_ == OperationDefinition.class) {
2251        OperationDefinition od = operations.get(uri, version);
2252        return (T) od;
2253      } else if (class_ == Questionnaire.class) {
2254        return (T) questionnaires.get(uri, version, pvlist);
2255      } else if (class_ == SearchParameter.class) {
2256        SearchParameter res = searchParameters.get(uri, version, pvlist);
2257        return (T) res;
2258      }
2259      if (class_ == CodeSystem.class && codeSystems.has(uri)) { 
2260        return (T) codeSystems.get(uri, version, pvlist);
2261      }
2262      if (class_ == ValueSet.class && valueSets.has(uri)) {
2263        return (T) valueSets.get(uri, version, pvlist);
2264      } 
2265      
2266      if (class_ == Questionnaire.class) {
2267        return (T) questionnaires.get(uri, version, pvlist);
2268      } 
2269      if (supportedCodeSystems.contains(uri)) {
2270        return null;
2271      } 
2272      throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri));
2273    }
2274  }
2275
2276  private void populatePVList(List<String> pvlist, PackageInformation sourcePackage) {
2277    pvlist.add(sourcePackage.getVID());
2278    List<String> toadd = new ArrayList<>();
2279    do {
2280      toadd.clear();
2281      for (String s : pvlist) {
2282        PackageInformation pi = packages.get(s);
2283        if (pi != null) {
2284          for (String v : pi.getDependencies()) {
2285            if (!pvlist.contains(v) && !toadd.contains(v)) {
2286              toadd.add(v);
2287            }
2288          }
2289        }        
2290      }
2291      pvlist.addAll(toadd);
2292    } while (toadd.size() > 0);
2293  }
2294
2295  public PackageInformation getPackageForUrl(String uri) {
2296    if (uri == null) {
2297      return null;
2298    }
2299    uri = ProfileUtilities.sdNs(uri, null);
2300
2301    synchronized (lock) {
2302
2303      String version = null;
2304      if (uri.contains("|")) {
2305        version = uri.substring(uri.lastIndexOf("|")+1);
2306        uri = uri.substring(0, uri.lastIndexOf("|"));
2307      }
2308      if (uri.contains("#")) {
2309        uri = uri.substring(0, uri.indexOf("#"));
2310      } 
2311      if (structures.has(uri)) {
2312        return structures.getPackageInfo(uri, version);
2313      }        
2314      if (guides.has(uri)) {
2315        return guides.getPackageInfo(uri, version);
2316      } 
2317      if (capstmts.has(uri)) {
2318        return capstmts.getPackageInfo(uri, version);
2319      } 
2320      if (measures.has(uri)) {
2321        return measures.getPackageInfo(uri, version);
2322      } 
2323      if (libraries.has(uri)) {
2324        return libraries.getPackageInfo(uri, version);
2325      } 
2326      if (valueSets.has(uri)) {
2327        return valueSets.getPackageInfo(uri, version);
2328      } 
2329      if (codeSystems.has(uri)) {
2330        return codeSystems.getPackageInfo(uri, version);
2331      } 
2332      if (operations.has(uri)) {
2333        return operations.getPackageInfo(uri, version);
2334      } 
2335      if (searchParameters.has(uri)) {
2336        return searchParameters.getPackageInfo(uri, version);
2337      } 
2338      if (plans.has(uri)) {
2339        return plans.getPackageInfo(uri, version);
2340      } 
2341      if (maps.has(uri)) {
2342        return maps.getPackageInfo(uri, version);
2343      } 
2344      if (transforms.has(uri)) {
2345        return transforms.getPackageInfo(uri, version);
2346      } 
2347      if (actors.has(uri)) {
2348        return actors.getPackageInfo(uri, version);
2349      } 
2350      if (requirements.has(uri)) {
2351        return requirements.getPackageInfo(uri, version);
2352      } 
2353      if (questionnaires.has(uri)) {
2354        return questionnaires.getPackageInfo(uri, version);
2355      }         
2356      return null;
2357    }
2358  }
2359  
2360  @SuppressWarnings("unchecked")
2361  public <T extends Resource> T fetchResourceWithExceptionByVersion(String cls, String uri, String version, CanonicalResource source) throws FHIRException {
2362    if (uri == null) {
2363      return null;
2364    }
2365   
2366    if ("StructureDefinition".equals(cls)) {
2367      uri = ProfileUtilities.sdNs(uri, null);
2368    }
2369    synchronized (lock) {
2370
2371      if (version == null) {
2372        if (uri.contains("|")) {
2373          version = uri.substring(uri.lastIndexOf("|")+1);
2374          uri = uri.substring(0, uri.lastIndexOf("|"));
2375        }
2376      } else {
2377        boolean b = !uri.contains("|");
2378        assert b;
2379      }
2380      if (uri.contains("#")) {
2381        uri = uri.substring(0, uri.indexOf("#"));
2382      } 
2383      if (cls == null || "Resource".equals(cls)) {
2384        if (structures.has(uri)) {
2385          return (T) structures.get(uri, version);
2386        } 
2387        if (guides.has(uri)) {
2388          return (T) guides.get(uri, version);
2389        } 
2390        if (capstmts.has(uri)) {
2391          return (T) capstmts.get(uri, version);
2392        } 
2393        if (measures.has(uri)) {
2394          return (T) measures.get(uri, version);
2395        } 
2396        if (libraries.has(uri)) {
2397          return (T) libraries.get(uri, version);
2398        } 
2399        if (valueSets.has(uri)) {
2400          return (T) valueSets.get(uri, version);
2401        } 
2402        if (codeSystems.has(uri)) {
2403          return (T) codeSystems.get(uri, version);
2404        } 
2405        if (operations.has(uri)) {
2406          return (T) operations.get(uri, version);
2407        } 
2408        if (searchParameters.has(uri)) {
2409          return (T) searchParameters.get(uri, version);
2410        } 
2411        if (plans.has(uri)) {
2412          return (T) plans.get(uri, version);
2413        } 
2414        if (maps.has(uri)) {
2415          return (T) maps.get(uri, version);
2416        } 
2417        if (transforms.has(uri)) {
2418          return (T) transforms.get(uri, version);
2419        } 
2420        if (actors.has(uri)) {
2421          return (T) actors.get(uri, version);
2422        } 
2423        if (requirements.has(uri)) {
2424          return (T) requirements.get(uri, version);
2425        } 
2426        if (questionnaires.has(uri)) {
2427          return (T) questionnaires.get(uri, version);
2428        } 
2429        for (Map<String, ResourceProxy> rt : allResourcesById.values()) {
2430          for (ResourceProxy r : rt.values()) {
2431            if (uri.equals(r.getUrl())) {
2432              return (T) r.getResource();
2433            }
2434          }            
2435        }
2436      } else if ("ImplementationGuide".equals(cls)) {
2437        return (T) guides.get(uri, version);
2438      } else if ("CapabilityStatement".equals(cls)) {
2439        return (T) capstmts.get(uri, version);
2440      } else if ("Measure".equals(cls)) {
2441        return (T) measures.get(uri, version);
2442      } else if ("Library".equals(cls)) {
2443        return (T) libraries.get(uri, version);
2444      } else if ("StructureDefinition".equals(cls)) {
2445        return (T) structures.get(uri, version);
2446      } else if ("StructureMap".equals(cls)) {
2447        return (T) transforms.get(uri, version);
2448      } else if ("Requirements".equals(cls)) {
2449        return (T) requirements.get(uri, version);
2450      } else if ("ActorDefinition".equals(cls)) {
2451        return (T) actors.get(uri, version);
2452      } else if ("ValueSet".equals(cls)) {
2453        return (T) valueSets.get(uri, version);
2454      } else if ("CodeSystem".equals(cls)) {
2455        return (T) codeSystems.get(uri, version);
2456      } else if ("ConceptMap".equals(cls)) {
2457        return (T) maps.get(uri, version);
2458      } else if ("PlanDefinition".equals(cls)) {
2459        return (T) plans.get(uri, version);
2460      } else if ("OperationDefinition".equals(cls)) {
2461        OperationDefinition od = operations.get(uri, version);
2462        return (T) od;
2463      } else if ("Questionnaire.class".equals(cls)) {
2464        return (T) questionnaires.get(uri, version);
2465      } else if ("SearchParameter.class".equals(cls)) {
2466        SearchParameter res = searchParameters.get(uri, version);
2467        return (T) res;
2468      }
2469      if ("CodeSystem".equals(cls) && codeSystems.has(uri)) {
2470        return (T) codeSystems.get(uri, version);
2471      } 
2472      if ("ValueSet".equals(cls) && valueSets.has(uri)) {
2473        return (T) valueSets.get(uri, version);
2474      } 
2475      
2476      if ("Questionnaire".equals(cls)) {
2477        return (T) questionnaires.get(uri, version);
2478      } 
2479      if (cls == null) {
2480        if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
2481          return null;
2482        } 
2483
2484        // it might be a special URL.
2485        if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
2486          Resource res = null; // findTxValueSet(uri);
2487          if (res != null) {
2488            return (T) res;
2489          } 
2490        }
2491        return null;      
2492      }    
2493      if (supportedCodeSystems.contains(uri)) {
2494        return null;
2495      } 
2496      throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri));
2497    }
2498  }
2499  
2500  public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_, FhirPublication fhirVersion) {
2501    return fetchResourcesByType(class_);
2502  }
2503  
2504  @SuppressWarnings("unchecked")
2505  public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_) {
2506
2507    List<T> res = new ArrayList<>();
2508
2509    synchronized (lock) {
2510
2511      if (class_ == Resource.class || class_ == DomainResource.class || class_ == CanonicalResource.class || class_ == null) {
2512        res.addAll((List<T>) structures.getList());
2513        res.addAll((List<T>) guides.getList());
2514        res.addAll((List<T>) capstmts.getList());
2515        res.addAll((List<T>) measures.getList());
2516        res.addAll((List<T>) libraries.getList());
2517        res.addAll((List<T>) valueSets.getList());
2518        res.addAll((List<T>) codeSystems.getList());
2519        res.addAll((List<T>) operations.getList());
2520        res.addAll((List<T>) searchParameters.getList());
2521        res.addAll((List<T>) plans.getList());
2522        res.addAll((List<T>) maps.getList());
2523        res.addAll((List<T>) transforms.getList());
2524        res.addAll((List<T>) questionnaires.getList());
2525        res.addAll((List<T>) systems.getList());
2526        res.addAll((List<T>) actors.getList());
2527        res.addAll((List<T>) requirements.getList());
2528      } else if (class_ == ImplementationGuide.class) {
2529        res.addAll((List<T>) guides.getList());
2530      } else if (class_ == CapabilityStatement.class) {
2531        res.addAll((List<T>) capstmts.getList());
2532      } else if (class_ == Measure.class) {
2533        res.addAll((List<T>) measures.getList());
2534      } else if (class_ == Library.class) {
2535        res.addAll((List<T>) libraries.getList());
2536      } else if (class_ == StructureDefinition.class) {
2537        res.addAll((List<T>) structures.getList());
2538      } else if (class_ == StructureMap.class) {
2539        res.addAll((List<T>) transforms.getList());
2540      } else if (class_ == ValueSet.class) {
2541        res.addAll((List<T>) valueSets.getList());
2542      } else if (class_ == CodeSystem.class) {
2543        res.addAll((List<T>) codeSystems.getList());
2544      } else if (class_ == NamingSystem.class) {
2545        res.addAll((List<T>) systems.getList());
2546      } else if (class_ == ActorDefinition.class) {
2547        res.addAll((List<T>) actors.getList());
2548      } else if (class_ == Requirements.class) {
2549        res.addAll((List<T>) requirements.getList());
2550      } else if (class_ == ConceptMap.class) {
2551        res.addAll((List<T>) maps.getList());
2552      } else if (class_ == PlanDefinition.class) {
2553        res.addAll((List<T>) plans.getList());
2554      } else if (class_ == OperationDefinition.class) {
2555        res.addAll((List<T>) operations.getList());
2556      } else if (class_ == Questionnaire.class) {
2557        res.addAll((List<T>) questionnaires.getList());
2558      } else if (class_ == SearchParameter.class) {
2559        res.addAll((List<T>) searchParameters.getList());
2560      }
2561    }
2562    return res;
2563  }
2564
2565  private Set<String> notCanonical = new HashSet<String>();
2566
2567  protected IWorkerContextManager.IPackageLoadingTracker packageTracker;
2568  private boolean forPublication;
2569  private boolean cachingAllowed = true;
2570  private static boolean nsFailHasFailed;
2571
2572  public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) {
2573    return fetchResourceById(type, uri);
2574  }
2575  
2576  @Override
2577  public Resource fetchResourceById(String type, String uri) {
2578    synchronized (lock) {
2579      String[] parts = uri.split("\\/");
2580      if (!Utilities.noString(type) && parts.length == 1) {
2581        if (allResourcesById.containsKey(type)) {
2582          ResourceProxy res = allResourcesById.get(type).get(parts[0]);
2583          return res == null ? null : res.getResource();
2584        } else {
2585          return null;
2586        }
2587      }
2588      if (parts.length >= 2) {
2589        if (!Utilities.noString(type)) {
2590          if (!type.equals(parts[parts.length-2])) { 
2591            throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri));
2592          }
2593        }
2594        return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource();
2595      } else {
2596        throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri));
2597      }
2598    }
2599  }
2600
2601  public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource sourceForReference) {
2602    try {
2603      return fetchResourceWithException(class_, uri, sourceForReference);
2604    } catch (FHIRException e) {
2605      throw new Error(e);
2606    }    
2607  }
2608  
2609  public <T extends Resource> T fetchResource(Class<T> class_, String uri, FhirPublication fhirVersion) {
2610    return fetchResource(class_, uri);
2611  }
2612  
2613  public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
2614    try {
2615      return fetchResourceWithException(class_, uri, null);
2616    } catch (FHIRException e) {
2617      throw new Error(e);
2618    }
2619  }
2620
2621  public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version, FhirPublication fhirVersion) {
2622    return fetchResource(class_, uri, version);
2623  }
2624  public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) {
2625    try {
2626      return fetchResourceWithExceptionByVersion(class_, uri, version, null);
2627    } catch (FHIRException e) {
2628      throw new Error(e);
2629    }
2630  }
2631  
2632  @Override
2633  public <T extends Resource> boolean hasResource(Class<T> class_, String uri) {
2634    try {
2635      return fetchResourceWithException(class_, uri) != null;
2636    } catch (Exception e) {
2637      return false;
2638    }
2639  }
2640
2641  public <T extends Resource> boolean hasResource(String cls, String uri) {
2642    try {
2643      return fetchResourceWithException(cls, uri) != null;
2644    } catch (Exception e) {
2645      return false;
2646    }
2647  }
2648
2649  public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version) {
2650    try {
2651      return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null;
2652    } catch (Exception e) {
2653      return false;
2654    }
2655  }
2656
2657  public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version) {
2658    try {
2659      return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null;
2660    } catch (Exception e) {
2661      return false;
2662    }
2663  }
2664
2665  @Override
2666  public <T extends Resource> boolean hasResource(Class<T> class_, String uri, FhirPublication fhirVersion) {
2667    try {
2668      return fetchResourceWithException(class_, uri) != null;
2669    } catch (Exception e) {
2670      return false;
2671    }
2672  }
2673
2674  public <T extends Resource> boolean hasResource(String cls, String uri, FhirPublication fhirVersion) {
2675    try {
2676      return fetchResourceWithException(cls, uri) != null;
2677    } catch (Exception e) {
2678      return false;
2679    }
2680  }
2681
2682  public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version, FhirPublication fhirVersion) {
2683    try {
2684      return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null;
2685    } catch (Exception e) {
2686      return false;
2687    }
2688  }
2689
2690  public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version, FhirPublication fhirVersion) {
2691    try {
2692      return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null;
2693    } catch (Exception e) {
2694      return false;
2695    }
2696  }
2697
2698  public <T extends Resource> boolean hasResource(Class<T> class_, String uri, Resource sourceOfReference) {
2699    try {
2700      return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null;
2701    } catch (Exception e) {
2702      return false;
2703    }
2704  }
2705
2706  public void reportStatus(JsonObject json) {
2707    synchronized (lock) {
2708      json.addProperty("codeystem-count", codeSystems.size());
2709      json.addProperty("valueset-count", valueSets.size());
2710      json.addProperty("conceptmap-count", maps.size());
2711      json.addProperty("transforms-count", transforms.size());
2712      json.addProperty("structures-count", structures.size());
2713      json.addProperty("guides-count", guides.size());
2714      json.addProperty("statements-count", capstmts.size());
2715      json.addProperty("measures-count", measures.size());
2716      json.addProperty("libraries-count", libraries.size());
2717    }
2718  }
2719
2720
2721  public void dropResource(Resource r) throws FHIRException {
2722    dropResource(r.fhirType(), r.getId());   
2723  }
2724
2725  public void dropResource(String fhirType, String id) {
2726    synchronized (lock) {
2727
2728      Map<String, ResourceProxy> map = allResourcesById.get(fhirType);
2729      if (map == null) {
2730        map = new HashMap<String, ResourceProxy>();
2731        allResourcesById.put(fhirType, map);
2732      }
2733      if (map.containsKey(id)) {
2734        map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions)
2735      }
2736
2737      if (fhirType.equals("StructureDefinition")) {
2738        structures.drop(id);
2739        typeManager.reload();
2740      } else if (fhirType.equals("ImplementationGuide")) {
2741        guides.drop(id);
2742      } else if (fhirType.equals("CapabilityStatement")) {
2743        capstmts.drop(id);
2744      } else if (fhirType.equals("Measure")) {
2745        measures.drop(id);
2746      } else if (fhirType.equals("Library")) {
2747        libraries.drop(id);
2748      } else if (fhirType.equals("ValueSet")) {
2749        valueSets.drop(id);
2750      } else if (fhirType.equals("CodeSystem")) {
2751        codeSystems.drop(id);
2752      } else if (fhirType.equals("OperationDefinition")) {
2753        operations.drop(id);
2754      } else if (fhirType.equals("Questionnaire")) {
2755        questionnaires.drop(id);
2756      } else if (fhirType.equals("ConceptMap")) {
2757        maps.drop(id);
2758      } else if (fhirType.equals("StructureMap")) {
2759        transforms.drop(id);
2760      } else if (fhirType.equals("NamingSystem")) {
2761        systems.drop(id);
2762        systemUrlMap = null;
2763      } else if (fhirType.equals("ActorDefinition")) {
2764        actors.drop(id);
2765      } else if (fhirType.equals("Requirements")) {
2766        requirements.drop(id);
2767      }
2768    }
2769  }
2770
2771  private <T extends CanonicalResource> void dropMetadataResource(Map<String, T> map, String id) {
2772    T res = map.get(id);
2773    if (res != null) {
2774      map.remove(id);
2775      if (map.containsKey(res.getUrl())) {
2776        map.remove(res.getUrl());
2777      }
2778      if (res.getVersion() != null) {
2779        if (map.containsKey(res.getUrl()+"|"+res.getVersion())) {
2780          map.remove(res.getUrl()+"|"+res.getVersion());
2781        }
2782      }
2783    }
2784  }
2785
2786  
2787  public String listSupportedSystems() {
2788    synchronized (lock) {
2789      String sl = null;
2790      for (String s : supportedCodeSystems) {
2791        sl = sl == null ? s : sl + "\r\n" + s;
2792      }
2793      return sl;
2794    }
2795  }
2796
2797
2798  public int totalCount() {
2799    synchronized (lock) {
2800      return valueSets.size() +  maps.size() + structures.size() + transforms.size();
2801    }
2802  }
2803  
2804  public List<ConceptMap> listMaps() {
2805    List<ConceptMap> m = new ArrayList<ConceptMap>();
2806    synchronized (lock) {
2807      maps.listAll(m);
2808    }
2809    return m;
2810  }
2811  
2812  public List<StructureDefinition> listStructures() {
2813    List<StructureDefinition> m = new ArrayList<StructureDefinition>();
2814    synchronized (lock) {
2815      structures.listAll(m);    
2816    }
2817    return m;
2818  }
2819
2820  public StructureDefinition getStructure(String code) {
2821    synchronized (lock) {
2822      return structures.get(code);
2823    }
2824  }
2825
2826  private String getUri(NamingSystem ns) {
2827    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
2828      if (id.getType() == NamingSystemIdentifierType.URI) {
2829        return id.getValue();
2830      }
2831    }
2832    return null;
2833  }
2834
2835  private boolean hasOid(NamingSystem ns, String oid) {
2836    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
2837      if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid)) {
2838        return true;
2839      }
2840    }
2841    return false;
2842  }
2843
2844  public void cacheVS(JsonObject json, Map<String, ValidationResult> t) {
2845    synchronized (lock) {
2846      validationCache.put(json.get("url").getAsString(), t);
2847    }
2848  }
2849
2850  public SearchParameter getSearchParameter(String code) {
2851    synchronized (lock) {
2852      return searchParameters.get(code);
2853    }
2854  }
2855
2856  @Override
2857  public org.hl7.fhir.r5.context.ILoggingService getLogger() {
2858    return logger;
2859  }
2860
2861
2862  public StructureDefinition fetchTypeDefinition(String typeName, FhirPublication fhirVersion) {
2863    return fetchTypeDefinition(typeName);
2864  }
2865
2866  @Override
2867  public StructureDefinition fetchTypeDefinition(String typeName) {
2868    if (Utilities.isAbsoluteUrl(typeName)) {
2869      StructureDefinition res = fetchResource(StructureDefinition.class, typeName);
2870      if (res != null) {
2871        return res;
2872      }
2873    } 
2874    StructureDefinition p = typeManager.fetchTypeDefinition(typeName);
2875    if (p != null && !p.isGeneratedSnapshot()) {
2876      if (p.isGeneratingSnapshot()) {
2877        throw new FHIRException("Attempt to fetch the profile "+p.getVersionedUrl()+" while generating the snapshot for it");
2878      }
2879      try {
2880        if (logger.isDebugLogging()) {
2881          System.out.println("Generating snapshot for "+p.getVersionedUrl());
2882        }
2883        p.setGeneratingSnapshot(true);
2884        try {
2885          new ContextUtilities(this).generateSnapshot(p);
2886        } finally {
2887          p.setGeneratingSnapshot(false);      
2888        }
2889      } catch (Exception e) {
2890        // not sure what to do in this case?
2891        System.out.println("Unable to generate snapshot @5 for "+p.getVersionedUrl()+": "+e.getMessage());
2892        if (logger.isDebugLogging()) {
2893          e.printStackTrace();
2894        }
2895      }
2896    }
2897    return p;
2898  }
2899  
2900  @Override
2901  public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
2902    return typeManager.getDefinitions(typeName);
2903  }
2904
2905  @Override
2906  public List<StructureDefinition> fetchTypeDefinitions(String typeName, FhirPublication fhirVersion) {
2907    return typeManager.getDefinitions(typeName);
2908  }
2909
2910
2911  public boolean isPrimitiveType(String type) {
2912    return typeManager.isPrimitive(type);
2913  }
2914
2915  public boolean isDataType(String type) {
2916    return typeManager.isDataType(type);
2917  }
2918  
2919  public boolean isTlogging() {
2920    return tlogging;
2921  }
2922
2923  public void setTlogging(boolean tlogging) {
2924    this.tlogging = tlogging;
2925  }
2926
2927  public UcumService getUcumService() {
2928    return ucumService;
2929  }
2930
2931  public void setUcumService(UcumService ucumService) {
2932    this.ucumService = ucumService;
2933  }
2934
2935  public String getLinkForUrl(String corePath, String url) {
2936    if (url == null) {
2937      return null;
2938    }
2939    
2940    if (codeSystems.has(url)) {
2941      return codeSystems.get(url).getWebPath();
2942    }
2943
2944    if (valueSets.has(url)) {
2945      return valueSets.get(url).getWebPath();
2946    }
2947
2948    if (maps.has(url)) {
2949      return maps.get(url).getWebPath();
2950    }
2951    
2952    if (transforms.has(url)) {
2953      return transforms.get(url).getWebPath();
2954    }
2955    
2956    if (actors.has(url)) {
2957      return actors.get(url).getWebPath();
2958    }
2959    
2960    if (requirements.has(url)) {
2961      return requirements.get(url).getWebPath();
2962    }
2963    
2964    if (structures.has(url)) {
2965      return structures.get(url).getWebPath();
2966    }
2967    
2968    if (guides.has(url)) {
2969      return guides.get(url).getWebPath();
2970    }
2971    
2972    if (capstmts.has(url)) {
2973      return capstmts.get(url).getWebPath();
2974    }
2975    
2976    if (measures.has(url)) {
2977      return measures.get(url).getWebPath();
2978    }
2979
2980    if (libraries.has(url)) {
2981      return libraries.get(url).getWebPath();
2982    }
2983
2984    if (searchParameters.has(url)) {
2985      return searchParameters.get(url).getWebPath();
2986    }
2987        
2988    if (questionnaires.has(url)) {
2989      return questionnaires.get(url).getWebPath();
2990    }
2991
2992    if (operations.has(url)) {
2993      return operations.get(url).getWebPath();
2994    }
2995    
2996    if (plans.has(url)) {
2997      return plans.get(url).getWebPath();
2998    }
2999
3000    if (url.equals("http://loinc.org")) {
3001      return corePath+"loinc.html";
3002    }
3003    if (url.equals("http://unitsofmeasure.org")) {
3004      return corePath+"ucum.html";
3005    } 
3006    if (url.equals("http://snomed.info/sct")) {
3007      return corePath+"snomed.html";
3008    } 
3009    return null;
3010  }
3011
3012  public List<ImplementationGuide> allImplementationGuides() {
3013    List<ImplementationGuide> res = new ArrayList<>();
3014    guides.listAll(res);
3015    return res;
3016  }
3017
3018  @Override
3019  public Set<String> getBinaryKeysAsSet() { return binaries.keySet(); }
3020
3021  @Override
3022  public boolean hasBinaryKey(String binaryKey) {
3023    return binaries.containsKey(binaryKey);
3024  }
3025
3026  @Override
3027  public byte[] getBinaryForKey(String binaryKey) {
3028    return binaries.get(binaryKey);
3029  }
3030
3031  public void finishLoading(boolean genSnapshots) {
3032    if (!hasResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Base")) {
3033      cacheResource(ProfileUtilities.makeBaseDefinition(version));
3034    }
3035    if(genSnapshots) {
3036      for (StructureDefinition sd : listStructures()) {
3037        try {
3038          if (sd.getSnapshot().isEmpty()) { 
3039            new ContextUtilities(this).generateSnapshot(sd);
3040            //          new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd);
3041          }
3042        } catch (Exception e) {
3043          System.out.println("Unable to generate snapshot @1 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage());
3044          if (logger.isDebugLogging()) {
3045            e.printStackTrace();          
3046          }
3047        }
3048      }  
3049    }
3050    
3051    codeSystems.setVersion(version);
3052    valueSets.setVersion(version);
3053    maps.setVersion(version);
3054    transforms.setVersion(version);
3055    structures.setVersion(version);
3056    typeManager.reload();
3057    measures.setVersion(version);
3058    libraries.setVersion(version);
3059    guides.setVersion(version);
3060    capstmts.setVersion(version);
3061    searchParameters.setVersion(version);
3062    questionnaires.setVersion(version);
3063    operations.setVersion(version);
3064    plans.setVersion(version);
3065    systems.setVersion(version);
3066    actors.setVersion(version);
3067    requirements.setVersion(version);
3068  }
3069
3070  protected String tail(String url) {
3071    if (Utilities.noString(url)) {
3072      return "noname";
3073    }
3074    if (url.contains("/")) {
3075      return url.substring(url.lastIndexOf("/")+1);
3076    }
3077    return url;
3078  }
3079  
3080  public int getClientRetryCount() {
3081    return terminologyClientManager.getRetryCount();
3082  }
3083  
3084  public IWorkerContext setClientRetryCount(int value) {
3085    terminologyClientManager.setRetryCount(value);
3086    return this;
3087  }
3088
3089  public TerminologyClientManager getTxClientManager() {
3090    return terminologyClientManager;
3091  }
3092
3093  public String getCacheId() {
3094    return terminologyClientManager.getCacheId();
3095  }
3096
3097  public TimeTracker clock() {
3098    return clock;
3099  }
3100 
3101  public int countAllCaches() {
3102    return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() + 
3103        guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() + 
3104        systems.size()+ actors.size()+ requirements.size();
3105  }
3106
3107  public Set<String> getCodeSystemsUsed() {
3108    return codeSystemsUsed ;
3109  }
3110 
3111  public IWorkerContextManager.ICanonicalResourceLocator getLocator() {
3112    return locator;
3113  }
3114
3115  public void setLocator(IWorkerContextManager.ICanonicalResourceLocator locator) {
3116    this.locator = locator;
3117  }
3118
3119  public String getUserAgent() {
3120    return userAgent;
3121  }
3122
3123  protected void setUserAgent(String userAgent) {
3124    this.userAgent = userAgent;
3125    terminologyClientManager.setUserAgent(userAgent);
3126  }
3127
3128
3129  public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() {
3130    return packageTracker;
3131  }
3132  
3133  public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker packageTracker) {
3134    this.packageTracker = packageTracker;
3135    return this;
3136  }
3137  
3138
3139  @Override
3140  public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps) {
3141    // TODO Auto-generated method stub
3142    return new PEBuilder(this, elementProps, fixedProps);
3143  }
3144  
3145  public boolean isForPublication() {
3146    return forPublication;
3147  }
3148  
3149  public void setForPublication(boolean value) {
3150    forPublication = value;
3151  }
3152
3153  public boolean isCachingAllowed() {
3154    return cachingAllowed;
3155  }
3156
3157  public void setCachingAllowed(boolean cachingAllowed) {
3158    this.cachingAllowed = cachingAllowed;
3159  }
3160
3161  @Override
3162  public OIDSummary urlsForOid(String oid, String resourceType) {
3163    OIDSummary set = urlsForOid(oid, resourceType, true);
3164    if (set.getDefinitions().size() > 1) {
3165      set = urlsForOid(oid, resourceType, false);
3166    }
3167    return set;
3168  }
3169  
3170  public OIDSummary urlsForOid(String oid, String resourceType, boolean retired) {
3171    OIDSummary summary = new OIDSummary();
3172    if (oid != null) {
3173      if (oidCacheManual.containsKey(oid)) {
3174        summary.addOIDs(oidCacheManual.get(oid));
3175      }
3176      for (OIDSource os : oidSources) {
3177        if (os.db == null) {
3178          os.db = connectToOidSource(os.folder);
3179        }
3180        if (os.db != null) {
3181          try {
3182            PreparedStatement psql = resourceType == null ?
3183                os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where OID = ?") :
3184                os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where TYPE = '"+resourceType+"' and OID = ?");
3185            psql.setString(1, oid);
3186            ResultSet rs = psql.executeQuery();
3187            while (rs.next()) {
3188              if (retired || !"retired".equals(rs.getString(4))) {
3189                String rt = rs.getString(1);
3190                String url = rs.getString(2);
3191                String version = rs.getString(3);
3192                String status = rs.getString(4);
3193                summary.addOID(new OIDDefinition(rt, oid, url, version, os.pid, status));
3194              }
3195            }
3196          } catch (Exception e) {
3197            // nothing, there would alreagy have been an error
3198  //          e.printStackTrace();
3199          }
3200        }
3201      }      
3202  
3203      switch (oid) {
3204      case "2.16.840.1.113883.6.1" :
3205        summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.1", "http://loinc.org", null, null, null));
3206        break;
3207      case "2.16.840.1.113883.6.8" :
3208        summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.8", "http://unitsofmeasure.org", null, null, null));
3209        break;
3210      case "2.16.840.1.113883.6.96" :
3211        summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.96", "http://snomed.info/sct", null, null, null));
3212        break;
3213      default:
3214      }
3215    }
3216    summary.sort();
3217    return summary;
3218  }
3219
3220  private Connection connectToOidSource(String folder) {
3221    try {
3222      File ff = ManagedFileAccess.file(folder);
3223      File of = ManagedFileAccess.file(Utilities.path(ff.getAbsolutePath(), ".oid-map-2.db"));
3224      if (!of.exists()) {
3225        OidIndexBuilder oidBuilder = new OidIndexBuilder(ff, of);
3226        oidBuilder.build();
3227      }
3228      return DriverManager.getConnection("jdbc:sqlite:"+of.getAbsolutePath());
3229    } catch (Exception e) {
3230      return null;
3231    }
3232  }
3233
3234
3235  public void unload() {
3236
3237    codeSystems.unload();
3238    valueSets.unload();
3239    maps.unload();
3240    transforms.unload();
3241    structures.unload();
3242    typeManager.unload();
3243    measures.unload();
3244    libraries.unload();
3245    guides.unload();
3246    capstmts.unload();
3247    searchParameters.unload();
3248    questionnaires.unload();
3249    operations.unload();
3250    plans.unload();
3251    actors.unload();
3252    requirements.unload();
3253    systems.unload();
3254
3255    binaries.clear();
3256    validationCache.clear();
3257    txCache.unload();
3258}
3259  
3260  private <T extends Resource> T doFindTxResource(Class<T> class_, String canonical) {
3261    // well, we haven't found it locally. We're going look it up
3262    if (class_ == ValueSet.class) {
3263      SourcedValueSet svs = null;
3264      if (txCache.hasValueSet(canonical)) {
3265        svs = txCache.getValueSet(canonical);
3266      } else {
3267        svs = terminologyClientManager.findValueSetOnServer(canonical);
3268        txCache.cacheValueSet(canonical, svs);
3269      }
3270      if (svs != null) {
3271        String web = ToolingExtensions.readStringExtension(svs.getVs(), ToolingExtensions.EXT_WEB_SOURCE);
3272        if (web == null) {
3273          web = Utilities.pathURL(svs.getServer(), "ValueSet", svs.getVs().getIdBase());
3274        }
3275        svs.getVs().setWebPath(web);
3276        svs.getVs().setUserData("External.Link", svs.getServer()); // so we can render it differently
3277      }      
3278      if (svs == null) {
3279        return null;
3280      } else {
3281        cacheResource(svs.getVs());
3282        return (T) svs.getVs();
3283      }
3284    } else if (class_ == CodeSystem.class) {
3285      SourcedCodeSystem scs = null;
3286      if (txCache.hasCodeSystem(canonical)) {
3287        scs = txCache.getCodeSystem(canonical);
3288      } else {
3289        scs = terminologyClientManager.findCodeSystemOnServer(canonical);
3290        txCache.cacheCodeSystem(canonical, scs);
3291      }
3292      if (scs != null) {
3293        String web = ToolingExtensions.readStringExtension(scs.getCs(), ToolingExtensions.EXT_WEB_SOURCE);
3294        if (web == null) {
3295          web = Utilities.pathURL(scs.getServer(), "ValueSet", scs.getCs().getIdBase());
3296        }
3297        scs.getCs().setWebPath(web);
3298        scs.getCs().setUserData("External.Link", scs.getServer()); // so we can render it differently
3299      }      
3300      if (scs == null) {
3301        return null;
3302      } else {
3303        cacheResource(scs.getCs());
3304        return (T) scs.getCs();
3305      }
3306    } else {
3307      throw new Error("Not supported");
3308    }
3309  }
3310
3311  public <T extends Resource> T findTxResource(Class<T> class_, String canonical, Resource sourceOfReference) {
3312    if (canonical == null) {
3313      return null;
3314    }
3315   T result = fetchResource(class_, canonical, sourceOfReference);
3316   if (result == null) {
3317     result = doFindTxResource(class_, canonical);
3318   }
3319   return result;
3320  }
3321
3322  public <T extends Resource> T findTxResource(Class<T> class_, String canonical) {
3323    if (canonical == null) {
3324      return null;
3325    }
3326    T result = fetchResource(class_, canonical);
3327    if (result == null) {
3328      result = doFindTxResource(class_, canonical);
3329    }
3330    return result;
3331  }
3332  
3333  public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version) {
3334    if (canonical == null) {
3335      return null;
3336    }
3337    T result = fetchResource(class_, canonical, version);
3338    if (result == null) {
3339      result = doFindTxResource(class_, canonical+"|"+version);
3340    }
3341    return result;
3342  }
3343
3344  @Override
3345  public <T extends Resource> List<T> fetchResourcesByUrl(Class<T> class_, String uri) {
3346    List<T> res = new ArrayList<>();
3347    if (uri != null && !uri.startsWith("#")) {
3348      if (class_ == StructureDefinition.class) {
3349        uri = ProfileUtilities.sdNs(uri, null);
3350      }
3351      assert !uri.contains("|");
3352      if (uri.contains("#")) {
3353        uri = uri.substring(0, uri.indexOf("#"));
3354      } 
3355      synchronized (lock) {
3356        if (class_ == Resource.class || class_ == null) {
3357          for (Map<String, ResourceProxy> rt : allResourcesById.values()) {
3358            for (ResourceProxy r : rt.values()) {
3359              if (uri.equals(r.getUrl())) {
3360                res.add((T) r.getResource());
3361              }
3362            }            
3363          }  
3364        }
3365        if (class_ == ImplementationGuide.class || class_ == Resource.class || class_ == null) {
3366          for (ImplementationGuide cr : guides.getForUrl(uri)) {
3367            res.add((T) cr);
3368          } 
3369        } else if (class_ == CapabilityStatement.class || class_ == Resource.class || class_ == null) {
3370          for (CapabilityStatement cr : capstmts.getForUrl(uri)) {
3371            res.add((T) cr);
3372          } 
3373        } else if (class_ == Measure.class || class_ == Resource.class || class_ == null) {
3374          for (Measure cr : measures.getForUrl(uri)) {
3375            res.add((T) cr);
3376          } 
3377        } else if (class_ == Library.class || class_ == Resource.class || class_ == null) {
3378          for (Library cr : libraries.getForUrl(uri)) {
3379            res.add((T) cr);
3380          } 
3381        } else if (class_ == StructureDefinition.class || class_ == Resource.class || class_ == null) {
3382          for (StructureDefinition cr : structures.getForUrl(uri)) {
3383            res.add((T) cr);
3384          } 
3385        } else if (class_ == StructureMap.class || class_ == Resource.class || class_ == null) {
3386          for (StructureMap cr : transforms.getForUrl(uri)) {
3387            res.add((T) cr);
3388          } 
3389        } else if (class_ == NamingSystem.class || class_ == Resource.class || class_ == null) {
3390          for (NamingSystem cr : systems.getForUrl(uri)) {
3391            res.add((T) cr);
3392          } 
3393        } else if (class_ == ValueSet.class || class_ == Resource.class || class_ == null) {
3394          for (ValueSet cr : valueSets.getForUrl(uri)) {
3395            res.add((T) cr);
3396          } 
3397        } else if (class_ == CodeSystem.class || class_ == Resource.class || class_ == null) {
3398          for (CodeSystem cr : codeSystems.getForUrl(uri)) {
3399            res.add((T) cr);
3400          } 
3401        } else if (class_ == ConceptMap.class || class_ == Resource.class || class_ == null) {
3402          for (ConceptMap cr : maps.getForUrl(uri)) {
3403            res.add((T) cr);
3404          } 
3405        } else if (class_ == ActorDefinition.class || class_ == Resource.class || class_ == null) {
3406          for (ActorDefinition cr : actors.getForUrl(uri)) {
3407            res.add((T) cr);
3408          } 
3409        } else if (class_ == Requirements.class || class_ == Resource.class || class_ == null) {
3410          for (Requirements cr : requirements.getForUrl(uri)) {
3411            res.add((T) cr);
3412          } 
3413        } else if (class_ == PlanDefinition.class || class_ == Resource.class || class_ == null) {
3414          for (PlanDefinition cr : plans.getForUrl(uri)) {
3415            res.add((T) cr);
3416          } 
3417        } else if (class_ == OperationDefinition.class || class_ == Resource.class || class_ == null) {
3418          for (OperationDefinition cr : operations.getForUrl(uri)) {
3419            res.add((T) cr);
3420          } 
3421        } else if (class_ == Questionnaire.class || class_ == Resource.class || class_ == null) {
3422          for (Questionnaire cr : questionnaires.getForUrl(uri)) {
3423            res.add((T) cr);
3424          } 
3425        } else if (class_ == SearchParameter.class || class_ == Resource.class || class_ == null) {
3426          for (SearchParameter cr : searchParameters.getForUrl(uri)) {
3427            res.add((T) cr);
3428          } 
3429        }
3430      }
3431    }
3432    return res;
3433  }
3434
3435}