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