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