
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); 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())); 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 { 1873 set.add(TerminologyClientManager.UNRESOLVED_VALUESET); 1874 } 1875 } 1876 } 1877 1878 private void findRelevantSystems(Set<String> set, Coding c) { 1879 if (c.hasSystem()) { 1880 if (c.hasVersion()) { 1881 set.add(c.getSystem()+"|"+c.getVersion()); 1882 } else { 1883 set.add(c.getSystem()); 1884 } 1885 } 1886 } 1887 1888 protected ValidationResult validateOnServer(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException { 1889 return validateOnServer2(tc, vs, pin, options, null); 1890 } 1891 1892 protected ValidationResult validateOnServer2(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options, Set<String> systems) throws FHIRException { 1893 1894 if (vs != null) { 1895 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1896 codeSystemsUsed.add(inc.getSystem()); 1897 } 1898 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1899 codeSystemsUsed.add(inc.getSystem()); 1900 } 1901 } 1902 1903 addServerValidationParameters(null, tc, vs, pin, options, systems); 1904 1905 if (txLog != null) { 1906 txLog.clearLastId(); 1907 } 1908 if (tc == null) { 1909 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1910 } 1911 Parameters pOut; 1912 if (vs == null) { 1913 pOut = tc.getClient().validateCS(pin); 1914 } else { 1915 pOut = tc.getClient().validateVS(pin); 1916 } 1917 return processValidationResult(pOut, vs == null ? null : vs.getUrl(), tc.getClient().getAddress()); 1918 } 1919 1920 protected void addServerValidationParameters(ValueSetProcessBase.TerminologyOperationDetails opCtxt, TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options) { 1921 addServerValidationParameters(opCtxt, terminologyClientContext, vs, pin, options, null); 1922 } 1923 1924 protected void addServerValidationParameters(ValueSetProcessBase.TerminologyOperationDetails opCtxt, TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options, Set<String> systems) { 1925 boolean cache = false; 1926 if (vs != null) { 1927 if (terminologyClientContext != null && terminologyClientContext.isTxCaching() && terminologyClientContext.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) { 1928 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl())); 1929 if (vs.hasVersion()) { 1930 pin.addParameter().setName("valueSetVersion").setValue(new StringType(vs.getVersion())); 1931 } 1932 } else if (options.getVsAsUrl()){ 1933 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl())); 1934 } else { 1935 if (vs.hasCompose() && vs.hasExpansion()) { 1936 vs = vs.copy(); 1937 vs.setExpansion(null); 1938 } 1939 pin.addParameter().setName("valueSet").setResource(vs); 1940 if (vs.getUrl() != null) { 1941 terminologyClientContext.getCached().add(vs.getUrl()+"|"+ vs.getVersion()); 1942 } 1943 } 1944 cache = true; 1945 addDependentResources(opCtxt, terminologyClientContext, pin, vs); 1946 } 1947 if (systems != null) { 1948 for (String s : systems) { 1949 cache = addDependentCodeSystem(opCtxt, terminologyClientContext, pin, s, null) || cache; 1950 } 1951 } 1952 pin.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); 1953 for (ParametersParameterComponent pp : pin.getParameter()) { 1954 if (pp.getName().equals("profile")) { 1955 throw new Error(formatMessage(I18nConstants.CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT)); 1956 } 1957 } 1958 if (expansionParameters.get() == null) { 1959 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1960 } 1961 String defLang = null; 1962 for (ParametersParameterComponent pp : expansionParameters.get().getParameter()) { 1963 if ("defaultDisplayLanguage".equals(pp.getName())) { 1964 defLang = pp.getValue().primitiveValue(); 1965 } else if (!pin.hasParameter(pp.getName())) { 1966 pin.addParameter(pp); 1967 } else if ("displayLanguage".equals(pp.getName())) { 1968 pin.setParameter(pp); 1969 } 1970 } 1971 if (defLang != null && !pin.hasParameter("displayLanguage")) { 1972 pin.addParameter().setName("displayLanguage").setValue(new CodeType(defLang)); 1973 } 1974 1975 if (options.isDisplayWarningMode()) { 1976 pin.addParameter("mode","lenient-display-validation"); 1977 } 1978 pin.addParameter("diagnostics", true); 1979 } 1980 1981 private boolean addDependentResources(ValueSetProcessBase.TerminologyOperationDetails opCtxt, TerminologyClientContext tc, Parameters pin, ValueSet vs) { 1982 boolean cache = false; 1983 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1984 cache = addDependentResources(opCtxt, tc, pin, inc, vs) || cache; 1985 } 1986 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1987 cache = addDependentResources(opCtxt, tc, pin, inc, vs) || cache; 1988 } 1989 return cache; 1990 } 1991 1992 private boolean addDependentResources(ValueSetProcessBase.TerminologyOperationDetails opCtxt, TerminologyClientContext tc, Parameters pin, ConceptSetComponent inc, Resource src) { 1993 boolean cache = false; 1994 for (CanonicalType c : inc.getValueSet()) { 1995 ValueSet vs = fetchResource(ValueSet.class, c.getValue(), null, src); 1996 if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) { 1997 cache = checkAddToParams(tc, pin, vs) || cache; 1998 addDependentResources(opCtxt, tc, pin, vs); 1999 for (Extension ext : vs.getExtensionsByUrl(ExtensionDefinitions.EXT_VS_CS_SUPPL_NEEDED)) { 2000 if (ext.hasValueCanonicalType()) { 2001 String url = ext.getValueCanonicalType().asStringValue(); 2002 CodeSystem supp = fetchResource(CodeSystem.class, url); 2003 if (supp != null) { 2004 if (opCtxt != null) { 2005 opCtxt.seeSupplement(supp); 2006 } 2007 cache = checkAddToParams(tc, pin, supp) || cache; 2008 } 2009 } 2010 } 2011 } 2012 } 2013 String sys = inc.getSystem(); 2014 cache = addDependentCodeSystem(opCtxt, tc, pin, sys, src) || cache; 2015 return cache; 2016 } 2017 2018 public boolean addDependentCodeSystem(ValueSetProcessBase.TerminologyOperationDetails opCtxt, TerminologyClientContext tc, Parameters pin, String sys, Resource src) { 2019 boolean cache = false; 2020 CodeSystem cs = fetchResource(CodeSystem.class, sys, null, src); 2021 if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) { 2022 cache = checkAddToParams(tc, pin, cs) || cache; 2023 } 2024 for (CodeSystem supp : codeSystems.getSupplements(cs)) { 2025 if (opCtxt != null) { 2026 opCtxt.seeSupplement(supp); 2027 } 2028 if (!hasCanonicalResource(pin, "tx-resource", supp.getVUrl()) ) { 2029 cache = checkAddToParams(tc, pin, supp) || cache; 2030 } 2031 } 2032 if (sys != null) { 2033 // we also have to look at this by version because the resource might not be versioned or we might not have a copy 2034 for (CodeSystem supp : codeSystems.getSupplements(sys)) { 2035 2036 if (opCtxt != null) { 2037 opCtxt.seeSupplement(supp); 2038 } 2039 if (!hasCanonicalResource(pin, "tx-resource", supp.getVUrl()) ) { 2040 cache = checkAddToParams(tc, pin, supp) || cache; 2041 } 2042 } 2043 if (!sys.contains("!")) { 2044 sys = getFixedVersion(sys, pin); 2045 if (sys != null) { 2046 for (CodeSystem supp : codeSystems.getSupplements(sys)) { 2047 if (opCtxt != null) { 2048 opCtxt.seeSupplement(supp); 2049 } 2050 if (!hasCanonicalResource(pin, "tx-resource", supp.getVUrl()) ) { 2051 cache = checkAddToParams(tc, pin, supp) || cache; 2052 } 2053 } 2054 } 2055 } 2056 } 2057 return cache; 2058 } 2059 2060 private String getFixedVersion(String sys, Parameters pin) { 2061 for (ParametersParameterComponent p : pin.getParameter()) { 2062 if (Utilities.existsInList(p.getName(), "system-version", "force-system-version", "default-system-version")) { 2063 if (p.hasValuePrimitive() && p.getValue().primitiveValue() != null && p.getValue().primitiveValue().startsWith(sys)) { 2064 return p.getValue().primitiveValue(); 2065 } 2066 } 2067 } 2068 return null; 2069 } 2070 2071 private boolean checkAddToParams(TerminologyClientContext tc, Parameters pin, CanonicalResource cr) { 2072 boolean cache = false; 2073 boolean addToParams = false; 2074 if (tc.usingCache()) { 2075 if (!tc.alreadyCached(cr)) { 2076 tc.addToCache(cr); 2077 2078 logger.logDebugMessage(LogCategory.CONTEXT, "add to cache: "+cr.getVUrl()); 2079 2080 addToParams = true; 2081 cache = true; 2082 } else { 2083 logger.logDebugMessage(LogCategory.CONTEXT,"already cached: "+cr.getVUrl()); 2084 } 2085 } else { 2086 addToParams = true; 2087 } 2088 if (addToParams) { 2089 pin.addParameter().setName("tx-resource").setResource(cr); 2090 } 2091 return cache; 2092 } 2093 2094 private boolean hasCanonicalResource(Parameters pin, String name, String vUrl) { 2095 for (ParametersParameterComponent p : pin.getParameter()) { 2096 if (name.equals(p.getName()) && p.hasResource() && 2097 p.getResource() instanceof CanonicalResource && vUrl.equals(((CanonicalResource) p.getResource()).getVUrl())) { 2098 return true; 2099 } 2100 } 2101 return false; 2102 } 2103 2104 public ValidationResult processValidationResult(Parameters pOut, String vs, String server) { 2105 boolean ok = false; 2106 String message = "No Message returned"; 2107 String display = null; 2108 String system = null; 2109 String code = null; 2110 String version = null; 2111 boolean inactive = false; 2112 String status = null; 2113 String diagnostics = null; 2114 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 2115 Set<String> unknownSystems = new HashSet<>(); 2116 2117 TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN; 2118 for (ParametersParameterComponent p : pOut.getParameter()) { 2119 if (p.hasValue()) { 2120 if (p.getName().equals("result")) { 2121 ok = ((BooleanType) p.getValue()).getValue().booleanValue(); 2122 } else if (p.getName().equals("message")) { 2123 message = p.getValue().primitiveValue(); 2124 } else if (p.getName().equals("display")) { 2125 display = p.getValue().primitiveValue(); 2126 } else if (p.getName().equals("system")) { 2127 system = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2128 } else if (p.getName().equals("version")) { 2129 version = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2130 } else if (p.getName().equals("code")) { 2131 code = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2132 } else if (p.getName().equals("diagnostics")) { 2133 diagnostics = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2134 } else if (p.getName().equals("inactive")) { 2135 inactive = "true".equals(((PrimitiveType<?>) p.getValue()).asStringValue()); 2136 } else if (p.getName().equals("status")) { 2137 status = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2138 } else if (p.getName().equals("x-caused-by-unknown-system")) { 2139 String unkSystem = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2140 if (unkSystem != null && unkSystem.contains("|")) { 2141 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED_VERSION; 2142 system = unkSystem.substring(0, unkSystem.indexOf("|")); 2143 version = unkSystem.substring(unkSystem.indexOf("|")+1); 2144 } else { 2145 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 2146 unknownSystems.add(unkSystem); 2147 } 2148 } else if (p.getName().equals("x-unknown-system")) { 2149 unknownSystems.add(((PrimitiveType<?>) p.getValue()).asStringValue()); 2150 } else if (p.getName().equals("warning-withdrawn")) { 2151 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2152 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 2153 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_WITHDRAWN : I18nConstants.MSG_WITHDRAWN_SRC, msg, vs, impliedType(msg))); 2154 issues.add(iss); 2155 } else if (p.getName().equals("warning-deprecated")) { 2156 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2157 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 2158 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DEPRECATED : I18nConstants.MSG_DEPRECATED_SRC, msg, vs, impliedType(msg))); 2159 issues.add(iss); 2160 } else if (p.getName().equals("warning-retired")) { 2161 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2162 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 2163 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_RETIRED : I18nConstants.MSG_RETIRED_SRC, msg, vs, impliedType(msg))); 2164 issues.add(iss); 2165 } else if (p.getName().equals("warning-experimental")) { 2166 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2167 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 2168 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_EXPERIMENTAL : I18nConstants.MSG_EXPERIMENTAL_SRC, msg, vs, impliedType(msg))); 2169 issues.add(iss); 2170 } else if (p.getName().equals("warning-draft")) { 2171 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 2172 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 2173 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DRAFT : I18nConstants.MSG_DRAFT_SRC, msg, vs, impliedType(msg))); 2174 issues.add(iss); 2175 } else if (p.getName().equals("cause")) { 2176 try { 2177 IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue()); 2178 if (it == IssueType.UNKNOWN) { 2179 err = TerminologyServiceErrorClass.UNKNOWN; 2180 } else if (it == IssueType.NOTFOUND) { 2181 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 2182 } else if (it == IssueType.NOTSUPPORTED) { 2183 err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED; 2184 } else { 2185 err = null; 2186 } 2187 } catch (FHIRException e) { 2188 } 2189 } 2190 } else if (p.hasResource()) { 2191 if (p.getName().equals("issues")) { 2192 OperationOutcome oo = (OperationOutcome) p.getResource(); 2193 for (OperationOutcomeIssueComponent iss : oo.getIssue()) { 2194 iss.addExtension(ExtensionDefinitions.EXT_ISSUE_SERVER, new UrlType(server)); 2195 issues.add(iss); 2196 } 2197 } else { 2198 // nothing? 2199 } 2200 } 2201 } 2202 ValidationResult res = null; 2203 if (!ok) { 2204 res = new ValidationResult(IssueSeverity.ERROR, message, err, null).setTxLink(txLog == null ? null : txLog.getLastId()); 2205 if (code != null) { 2206 res.setDefinition(new ConceptDefinitionComponent().setDisplay(display).setCode(code)); 2207 res.setDisplay(display); 2208 } 2209 if (system != null) { 2210 res.setSystem(system); 2211 } 2212 if (version != null) { 2213 res.setVersion(version); 2214 } 2215 } else if (message != null && !message.equals("No Message returned")) { 2216 res = new ValidationResult(IssueSeverity.WARNING, message, system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog == null ? null : txLog.getLastId()); 2217 } else if (display != null) { 2218 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display).setTxLink(txLog == null ? null : txLog.getLastId()); 2219 } else { 2220 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setCode(code), null).setTxLink(txLog == null ? null : txLog.getLastId()); 2221 } 2222 res.setIssues(issues); 2223 res.setStatus(inactive, status); 2224 res.setUnknownSystems(unknownSystems); 2225 res.setServer(server); 2226 res.setParameters(pOut); 2227 res.setDiagnostics(diagnostics); 2228 return res; 2229 } 2230 2231 // -------------------------------------------------------------------------------------------------------------------------------------------------------- 2232 2233 private Object impliedType(String msg) { 2234 if (msg.contains("/CodeSystem")) { 2235 return "CodeSystem"; 2236 } 2237 if (msg.contains("/ValueSet")) { 2238 return "ValueSet"; 2239 } 2240 return "item"; 2241 } 2242 2243 public void initTxCache(String cachePath) throws FileNotFoundException, FHIRException, IOException { 2244 if (cachePath != null) { 2245 txCache = new TerminologyCache(lock, cachePath); 2246 initTxCache(txCache); 2247 } 2248 } 2249 2250 public void initTxCache(TerminologyCache cache) { 2251 txCache = cache; 2252 terminologyClientManager.setCache(txCache); 2253 } 2254 2255 public void clearTSCache(String url) throws Exception { 2256 txCache.removeCS(url); 2257 } 2258 2259 public boolean isCanRunWithoutTerminology() { 2260 return canRunWithoutTerminology; 2261 } 2262 2263 public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) { 2264 this.canRunWithoutTerminology = canRunWithoutTerminology; 2265 } 2266 2267 public void setLogger(@Nonnull org.hl7.fhir.r5.context.ILoggingService logger) { 2268 this.logger = logger; 2269 getTxClientManager().setLogger(logger); 2270 } 2271 2272 /** 2273 * Returns a copy of the expansion parameters used by this context. Note that because the return value is a copy, any 2274 * changes done to it will not be reflected in the context and any changes to the context will likewise not be 2275 * reflected in the return value after it is returned. If you need to change the expansion parameters, use 2276 * {@link #setExpansionParameters(Parameters)}. 2277 * 2278 * @return a copy of the expansion parameters 2279 */ 2280 public Parameters getExpansionParameters() { 2281 final Parameters parameters = expansionParameters.get(); 2282 return parameters == null ? null : parameters.copy(); 2283 } 2284 2285 public void setExpansionParameters(Parameters expansionParameters) { 2286 this.expansionParameters.set(expansionParameters); 2287 this.terminologyClientManager.setExpansionParameters(expansionParameters); 2288 } 2289 2290 @Override 2291 public boolean isNoTerminologyServer() { 2292 return noTerminologyServer || !terminologyClientManager.hasClient(); 2293 } 2294 2295 public void setNoTerminologyServer(boolean noTerminologyServer) { 2296 this.noTerminologyServer = noTerminologyServer; 2297 } 2298 2299 public String getName() { 2300 return name; 2301 } 2302 2303 public void setName(String name) { 2304 this.name = name; 2305 } 2306 2307 2308 public List<String> getResourceNames(FhirPublication fhirVersion) { 2309 return getResourceNames(); 2310 } 2311 2312 public Set<String> getResourceNamesAsSet(FhirPublication fhirVersion) { 2313 return getResourceNamesAsSet(); 2314 } 2315 2316 @Override 2317 public Set<String> getResourceNamesAsSet() { 2318 Set<String> res = new HashSet<String>(); 2319 res.addAll(getResourceNames()); 2320 return res; 2321 } 2322 2323 @Override 2324 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException { 2325 return fetchResourceWithException(class_, uri, null, null); 2326 } 2327 2328 public <T extends Resource> T fetchResourceWithException(String cls, String uri) throws FHIRException { 2329 return fetchResourceWithExceptionByVersion(cls, uri, null, null); 2330 } 2331 2332 public <T extends Resource> T fetchResourceByVersionWithException(Class<T> class_, String uri, String version) throws FHIRException { 2333 return fetchResourceWithExceptionByVersion(class_, uri, version, null); 2334 } 2335 2336 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, String version, Resource sourceForReference) throws FHIRException { 2337 return fetchResourceWithExceptionByVersion(class_, uri, version, sourceForReference); 2338 } 2339 2340 @SuppressWarnings("unchecked") 2341 public <T extends Resource> T fetchResourceWithExceptionByVersion(Class<T> class_, String uri, String version, Resource sourceForReference) throws FHIRException { 2342 if (uri == null) { 2343 return null; 2344 } 2345 if (uri.startsWith("#")) { 2346 if (sourceForReference != null && sourceForReference instanceof DomainResource) { 2347 for (Resource r : ((DomainResource) sourceForReference).getContained()) { 2348 if (r.getClass() == class_ &&( "#"+r.getIdBase()).equals(uri)) { 2349 if (r instanceof CanonicalResource) { 2350 CanonicalResource cr = (CanonicalResource) r; 2351 if (!cr.hasUrl()) { 2352 cr.setUrl(UUIDUtilities.makeUuidUrn()); 2353 } 2354 } 2355 return (T) r; 2356 } 2357 } 2358 } 2359 return null; 2360 } 2361 2362 if (QA_CHECK_REFERENCE_SOURCE) { 2363 // it can be tricky to trace the source of a reference correctly. The code isn't water tight, 2364 // particularly around snapshot generation. Enable this code to check that the references are 2365 // correct (but it's slow) 2366 if (sourceForReference != null && uri.contains("ValueSet")) { 2367 if (!ResourceUtilities.hasURL(uri, sourceForReference)) { 2368 log.warn("Claimed source doesn't have url in it: "+sourceForReference.fhirType()+"/"+sourceForReference.getIdPart()+" -> "+uri); 2369 } 2370 } 2371 } 2372 2373 List<String> pvlist = new ArrayList<>(); 2374 if (sourceForReference != null && sourceForReference.getSourcePackage() != null) { 2375 populatePVList(pvlist, sourceForReference.getSourcePackage()); 2376 } 2377 2378 if (class_ == StructureDefinition.class) { 2379 uri = ProfileUtilities.sdNs(uri, null); 2380 } 2381 synchronized (lock) { 2382 2383 if (version == null) { 2384 if (uri.contains("|")) { 2385 version = uri.substring(uri.lastIndexOf("|")+1); 2386 uri = uri.substring(0, uri.lastIndexOf("|")); 2387 } 2388 } else { 2389 assert !uri.contains("|"); 2390 } 2391 if (uri.contains("#")) { 2392 uri = uri.substring(0, uri.indexOf("#")); 2393 } 2394 if (class_ == Resource.class || class_ == null) { 2395 if (structures.has(uri)) { 2396 return (T) structures.getByPackage(uri, version, pvlist); 2397 } 2398 if (guides.has(uri)) { 2399 return (T) guides.getByPackage(uri, version, pvlist); 2400 } 2401 if (capstmts.has(uri)) { 2402 return (T) capstmts.getByPackage(uri, version, pvlist); 2403 } 2404 if (measures.has(uri)) { 2405 return (T) measures.getByPackage(uri, version, pvlist); 2406 } 2407 if (libraries.has(uri)) { 2408 return (T) libraries.getByPackage(uri, version, pvlist); 2409 } 2410 if (valueSets.has(uri)) { 2411 return (T) valueSets.getByPackage(uri, version, pvlist); 2412 } 2413 if (codeSystems.has(uri)) { 2414 return (T) codeSystems.getByPackage(uri, version, pvlist); 2415 } 2416 if (systems.has(uri)) { 2417 return (T) systems.getByPackage(uri, version, pvlist); 2418 } 2419 if (operations.has(uri)) { 2420 return (T) operations.getByPackage(uri, version, pvlist); 2421 } 2422 if (searchParameters.has(uri)) { 2423 return (T) searchParameters.getByPackage(uri, version, pvlist); 2424 } 2425 if (plans.has(uri)) { 2426 return (T) plans.getByPackage(uri, version, pvlist); 2427 } 2428 if (maps.has(uri)) { 2429 return (T) maps.getByPackage(uri, version, pvlist); 2430 } 2431 if (transforms.has(uri)) { 2432 return (T) transforms.getByPackage(uri, version, pvlist); 2433 } 2434 if (actors.has(uri)) { 2435 return (T) actors.getByPackage(uri, version, pvlist); 2436 } 2437 if (requirements.has(uri)) { 2438 return (T) requirements.getByPackage(uri, version, pvlist); 2439 } 2440 if (questionnaires.has(uri)) { 2441 return (T) questionnaires.getByPackage(uri, version, pvlist); 2442 } 2443 2444 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 2445 for (ResourceProxy r : rt.values()) { 2446 if (uri.equals(r.getUrl())) { 2447 if (version == null || version == r.getResource().getMeta().getVersionId()) { 2448 return (T) r.getResource(); 2449 } 2450 } 2451 } 2452 } 2453 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 2454 return null; 2455 } 2456 2457 // it might be a special URL. 2458// if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 2459// Resource res = null; // findTxValueSet(uri); 2460// if (res != null) { 2461// return (T) res; 2462// } 2463// } 2464 return null; 2465 } else if (class_ == ImplementationGuide.class) { 2466 return (T) guides.getByPackage(uri, version, pvlist); 2467 } else if (class_ == CapabilityStatement.class) { 2468 return (T) capstmts.getByPackage(uri, version, pvlist); 2469 } else if (class_ == Measure.class) { 2470 return (T) measures.getByPackage(uri, version, pvlist); 2471 } else if (class_ == Library.class) { 2472 return (T) libraries.getByPackage(uri, version, pvlist); 2473 } else if (class_ == StructureDefinition.class) { 2474 return (T) structures.getByPackage(uri, version, pvlist); 2475 } else if (class_ == StructureMap.class) { 2476 return (T) transforms.getByPackage(uri, version, pvlist); 2477 } else if (class_ == NamingSystem.class) { 2478 return (T) systems.getByPackage(uri, version, pvlist); 2479 } else if (class_ == ValueSet.class) { 2480 return (T) valueSets.getByPackage(uri, version, pvlist); 2481 } else if (class_ == CodeSystem.class) { 2482 return (T) codeSystems.getByPackage(uri, version, pvlist); 2483 } else if (class_ == ConceptMap.class) { 2484 return (T) maps.getByPackage(uri, version, pvlist); 2485 } else if (class_ == ActorDefinition.class) { 2486 return (T) actors.getByPackage(uri, version, pvlist); 2487 } else if (class_ == Requirements.class) { 2488 return (T) requirements.getByPackage(uri, version, pvlist); 2489 } else if (class_ == PlanDefinition.class) { 2490 return (T) plans.getByPackage(uri, version, pvlist); 2491 } else if (class_ == OperationDefinition.class) { 2492 OperationDefinition od = operations.get(uri, version); 2493 return (T) od; 2494 } else if (class_ == Questionnaire.class) { 2495 return (T) questionnaires.getByPackage(uri, version, pvlist); 2496 } else if (class_ == SearchParameter.class) { 2497 SearchParameter res = searchParameters.getByPackage(uri, version, pvlist); 2498 return (T) res; 2499 } 2500 if (class_ == CodeSystem.class && codeSystems.has(uri)) { 2501 return (T) codeSystems.getByPackage(uri, version, pvlist); 2502 } 2503 if (class_ == ValueSet.class && valueSets.has(uri)) { 2504 return (T) valueSets.getByPackage(uri, version, pvlist); 2505 } 2506 2507 if (class_ == Questionnaire.class) { 2508 return (T) questionnaires.getByPackage(uri, version, pvlist); 2509 } 2510 if (supportedCodeSystems.containsKey(uri)) { 2511 return null; 2512 } 2513 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 2514 } 2515 } 2516 2517 private void populatePVList(List<String> pvlist, PackageInformation sourcePackage) { 2518 pvlist.add(sourcePackage.getVID()); 2519 List<String> toadd = new ArrayList<>(); 2520 do { 2521 toadd.clear(); 2522 for (String s : pvlist) { 2523 PackageInformation pi = packages.get(s); 2524 if (pi != null) { 2525 for (String v : pi.getDependencies()) { 2526 if (!pvlist.contains(v) && !toadd.contains(v)) { 2527 toadd.add(v); 2528 } 2529 } 2530 } 2531 } 2532 pvlist.addAll(toadd); 2533 } while (toadd.size() > 0); 2534 } 2535 2536 public PackageInformation getPackageForUrl(String uri) { 2537 if (uri == null) { 2538 return null; 2539 } 2540 uri = ProfileUtilities.sdNs(uri, null); 2541 2542 synchronized (lock) { 2543 2544 String version = null; 2545 if (uri.contains("|")) { 2546 version = uri.substring(uri.lastIndexOf("|")+1); 2547 uri = uri.substring(0, uri.lastIndexOf("|")); 2548 } 2549 if (uri.contains("#")) { 2550 uri = uri.substring(0, uri.indexOf("#")); 2551 } 2552 if (structures.has(uri)) { 2553 return structures.getPackageInfo(uri, version); 2554 } 2555 if (guides.has(uri)) { 2556 return guides.getPackageInfo(uri, version); 2557 } 2558 if (capstmts.has(uri)) { 2559 return capstmts.getPackageInfo(uri, version); 2560 } 2561 if (measures.has(uri)) { 2562 return measures.getPackageInfo(uri, version); 2563 } 2564 if (libraries.has(uri)) { 2565 return libraries.getPackageInfo(uri, version); 2566 } 2567 if (valueSets.has(uri)) { 2568 return valueSets.getPackageInfo(uri, version); 2569 } 2570 if (codeSystems.has(uri)) { 2571 return codeSystems.getPackageInfo(uri, version); 2572 } 2573 if (operations.has(uri)) { 2574 return operations.getPackageInfo(uri, version); 2575 } 2576 if (searchParameters.has(uri)) { 2577 return searchParameters.getPackageInfo(uri, version); 2578 } 2579 if (plans.has(uri)) { 2580 return plans.getPackageInfo(uri, version); 2581 } 2582 if (maps.has(uri)) { 2583 return maps.getPackageInfo(uri, version); 2584 } 2585 if (transforms.has(uri)) { 2586 return transforms.getPackageInfo(uri, version); 2587 } 2588 if (actors.has(uri)) { 2589 return actors.getPackageInfo(uri, version); 2590 } 2591 if (requirements.has(uri)) { 2592 return requirements.getPackageInfo(uri, version); 2593 } 2594 if (questionnaires.has(uri)) { 2595 return questionnaires.getPackageInfo(uri, version); 2596 } 2597 return null; 2598 } 2599 } 2600 2601 @SuppressWarnings("unchecked") 2602 public <T extends Resource> T fetchResourceWithExceptionByVersion(String cls, String uri, String version, CanonicalResource source) throws FHIRException { 2603 if (uri == null) { 2604 return null; 2605 } 2606 2607 if ("StructureDefinition".equals(cls)) { 2608 uri = ProfileUtilities.sdNs(uri, null); 2609 } 2610 synchronized (lock) { 2611 2612 if (version == null) { 2613 if (uri.contains("|")) { 2614 version = uri.substring(uri.lastIndexOf("|")+1); 2615 uri = uri.substring(0, uri.lastIndexOf("|")); 2616 } 2617 } else { 2618 boolean b = !uri.contains("|"); 2619 assert b; 2620 } 2621 if (uri.contains("#")) { 2622 uri = uri.substring(0, uri.indexOf("#")); 2623 } 2624 if (cls == null || "Resource".equals(cls)) { 2625 if (structures.has(uri)) { 2626 return (T) structures.get(uri, version); 2627 } 2628 if (guides.has(uri)) { 2629 return (T) guides.get(uri, version); 2630 } 2631 if (capstmts.has(uri)) { 2632 return (T) capstmts.get(uri, version); 2633 } 2634 if (measures.has(uri)) { 2635 return (T) measures.get(uri, version); 2636 } 2637 if (libraries.has(uri)) { 2638 return (T) libraries.get(uri, version); 2639 } 2640 if (valueSets.has(uri)) { 2641 return (T) valueSets.get(uri, version); 2642 } 2643 if (codeSystems.has(uri)) { 2644 return (T) codeSystems.get(uri, version); 2645 } 2646 if (operations.has(uri)) { 2647 return (T) operations.get(uri, version); 2648 } 2649 if (searchParameters.has(uri)) { 2650 return (T) searchParameters.get(uri, version); 2651 } 2652 if (plans.has(uri)) { 2653 return (T) plans.get(uri, version); 2654 } 2655 if (maps.has(uri)) { 2656 return (T) maps.get(uri, version); 2657 } 2658 if (transforms.has(uri)) { 2659 return (T) transforms.get(uri, version); 2660 } 2661 if (actors.has(uri)) { 2662 return (T) actors.get(uri, version); 2663 } 2664 if (requirements.has(uri)) { 2665 return (T) requirements.get(uri, version); 2666 } 2667 if (questionnaires.has(uri)) { 2668 return (T) questionnaires.get(uri, version); 2669 } 2670 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 2671 for (ResourceProxy r : rt.values()) { 2672 if (uri.equals(r.getUrl())) { 2673 return (T) r.getResource(); 2674 } 2675 } 2676 } 2677 } else if ("ImplementationGuide".equals(cls)) { 2678 return (T) guides.get(uri, version); 2679 } else if ("CapabilityStatement".equals(cls)) { 2680 return (T) capstmts.get(uri, version); 2681 } else if ("Measure".equals(cls)) { 2682 return (T) measures.get(uri, version); 2683 } else if ("Library".equals(cls)) { 2684 return (T) libraries.get(uri, version); 2685 } else if ("StructureDefinition".equals(cls)) { 2686 return (T) structures.get(uri, version); 2687 } else if ("StructureMap".equals(cls)) { 2688 return (T) transforms.get(uri, version); 2689 } else if ("Requirements".equals(cls)) { 2690 return (T) requirements.get(uri, version); 2691 } else if ("ActorDefinition".equals(cls)) { 2692 return (T) actors.get(uri, version); 2693 } else if ("ValueSet".equals(cls)) { 2694 return (T) valueSets.get(uri, version); 2695 } else if ("CodeSystem".equals(cls)) { 2696 return (T) codeSystems.get(uri, version); 2697 } else if ("NamingSystem".equals(cls)) { 2698 return (T) systems.get(uri, version); 2699 } else if ("ConceptMap".equals(cls)) { 2700 return (T) maps.get(uri, version); 2701 } else if ("PlanDefinition".equals(cls)) { 2702 return (T) plans.get(uri, version); 2703 } else if ("OperationDefinition".equals(cls)) { 2704 OperationDefinition od = operations.get(uri, version); 2705 return (T) od; 2706 } else if ("Questionnaire".equals(cls)) { 2707 return (T) questionnaires.get(uri, version); 2708 } else if ("SearchParameter".equals(cls)) { 2709 SearchParameter res = searchParameters.get(uri, version); 2710 return (T) res; 2711 } 2712 if ("CodeSystem".equals(cls) && codeSystems.has(uri)) { 2713 return (T) codeSystems.get(uri, version); 2714 } 2715 if ("ValueSet".equals(cls) && valueSets.has(uri)) { 2716 return (T) valueSets.get(uri, version); 2717 } 2718 2719 if ("Questionnaire".equals(cls)) { 2720 return (T) questionnaires.get(uri, version); 2721 } 2722 if (cls == null) { 2723 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 2724 return null; 2725 } 2726 2727 // it might be a special URL. 2728 if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 2729 Resource res = null; // findTxValueSet(uri); 2730 if (res != null) { 2731 return (T) res; 2732 } 2733 } 2734 return null; 2735 } 2736 if (supportedCodeSystems.containsKey(uri)) { 2737 return null; 2738 } 2739 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 2740 } 2741 } 2742 2743 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_, FhirPublication fhirVersion) { 2744 return fetchResourcesByType(class_); 2745 } 2746 2747 @SuppressWarnings("unchecked") 2748 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_) { 2749 2750 List<T> res = new ArrayList<>(); 2751 2752 if (class_ == Resource.class || class_ == ValueSet.class) { 2753 if (!isAllowedToIterateTerminologyResources()) { 2754 // what's going on here? 2755 // it's not unusual to have >50k ValueSets in context. Iterating all of 2756 // them will cause every one of them to loaded through the lazy loading infrastructure. This 2757 // can consume upwards of 10GB of RAM. 2758 // 2759 // By default, the context won't let you do that. If you do want to do it, and take the performance hit, then 2760 // setAllowedToIterateTerminologyResources(true); 2761 throw new Error("This context is configured to not allow Iterating ValueSet resources due to performance concerns"); 2762 } 2763 } 2764 synchronized (lock) { 2765 2766 if (class_ == Resource.class || class_ == DomainResource.class || class_ == CanonicalResource.class || class_ == null) { 2767 res.addAll((List<T>) structures.getList()); 2768 res.addAll((List<T>) guides.getList()); 2769 res.addAll((List<T>) capstmts.getList()); 2770 res.addAll((List<T>) measures.getList()); 2771 res.addAll((List<T>) libraries.getList()); 2772 res.addAll((List<T>) valueSets.getList()); 2773 res.addAll((List<T>) codeSystems.getList()); 2774 res.addAll((List<T>) operations.getList()); 2775 res.addAll((List<T>) searchParameters.getList()); 2776 res.addAll((List<T>) plans.getList()); 2777 res.addAll((List<T>) maps.getList()); 2778 res.addAll((List<T>) transforms.getList()); 2779 res.addAll((List<T>) questionnaires.getList()); 2780 res.addAll((List<T>) systems.getList()); 2781 res.addAll((List<T>) actors.getList()); 2782 res.addAll((List<T>) requirements.getList()); 2783 } else if (class_ == ImplementationGuide.class) { 2784 res.addAll((List<T>) guides.getList()); 2785 } else if (class_ == CapabilityStatement.class) { 2786 res.addAll((List<T>) capstmts.getList()); 2787 } else if (class_ == Measure.class) { 2788 res.addAll((List<T>) measures.getList()); 2789 } else if (class_ == Library.class) { 2790 res.addAll((List<T>) libraries.getList()); 2791 } else if (class_ == StructureDefinition.class) { 2792 res.addAll((List<T>) structures.getList()); 2793 } else if (class_ == StructureMap.class) { 2794 res.addAll((List<T>) transforms.getList()); 2795 } else if (class_ == ValueSet.class) { 2796 res.addAll((List<T>) valueSets.getList()); 2797 } else if (class_ == CodeSystem.class) { 2798 res.addAll((List<T>) codeSystems.getList()); 2799 } else if (class_ == NamingSystem.class) { 2800 res.addAll((List<T>) systems.getList()); 2801 } else if (class_ == ActorDefinition.class) { 2802 res.addAll((List<T>) actors.getList()); 2803 } else if (class_ == Requirements.class) { 2804 res.addAll((List<T>) requirements.getList()); 2805 } else if (class_ == ConceptMap.class) { 2806 res.addAll((List<T>) maps.getList()); 2807 } else if (class_ == PlanDefinition.class) { 2808 res.addAll((List<T>) plans.getList()); 2809 } else if (class_ == OperationDefinition.class) { 2810 res.addAll((List<T>) operations.getList()); 2811 } else if (class_ == Questionnaire.class) { 2812 res.addAll((List<T>) questionnaires.getList()); 2813 } else if (class_ == SearchParameter.class) { 2814 res.addAll((List<T>) searchParameters.getList()); 2815 } 2816 } 2817 return res; 2818 } 2819 2820 @SuppressWarnings("unchecked") 2821 public <T extends Resource> List<T> fetchResourceVersionsByTypeAndUrl(Class<T> class_, String url) { 2822 2823 List<T> res = new ArrayList<>(); 2824 2825 synchronized (lock) { 2826 2827 if (class_ == Resource.class || class_ == DomainResource.class || class_ == CanonicalResource.class || class_ == null) { 2828 res.addAll((List<T>) structures.getVersionList(url)); 2829 res.addAll((List<T>) guides.getVersionList(url)); 2830 res.addAll((List<T>) capstmts.getVersionList(url)); 2831 res.addAll((List<T>) measures.getVersionList(url)); 2832 res.addAll((List<T>) libraries.getVersionList(url)); 2833 res.addAll((List<T>) valueSets.getVersionList(url)); 2834 res.addAll((List<T>) codeSystems.getVersionList(url)); 2835 res.addAll((List<T>) operations.getVersionList(url)); 2836 res.addAll((List<T>) searchParameters.getVersionList(url)); 2837 res.addAll((List<T>) plans.getVersionList(url)); 2838 res.addAll((List<T>) maps.getVersionList(url)); 2839 res.addAll((List<T>) transforms.getVersionList(url)); 2840 res.addAll((List<T>) questionnaires.getVersionList(url)); 2841 res.addAll((List<T>) systems.getVersionList(url)); 2842 res.addAll((List<T>) actors.getVersionList(url)); 2843 res.addAll((List<T>) requirements.getVersionList(url)); 2844 } else if (class_ == ImplementationGuide.class) { 2845 res.addAll((List<T>) guides.getVersionList(url)); 2846 } else if (class_ == CapabilityStatement.class) { 2847 res.addAll((List<T>) capstmts.getVersionList(url)); 2848 } else if (class_ == Measure.class) { 2849 res.addAll((List<T>) measures.getVersionList(url)); 2850 } else if (class_ == Library.class) { 2851 res.addAll((List<T>) libraries.getVersionList(url)); 2852 } else if (class_ == StructureDefinition.class) { 2853 res.addAll((List<T>) structures.getVersionList(url)); 2854 } else if (class_ == StructureMap.class) { 2855 res.addAll((List<T>) transforms.getVersionList(url)); 2856 } else if (class_ == ValueSet.class) { 2857 res.addAll((List<T>) valueSets.getVersionList(url)); 2858 } else if (class_ == CodeSystem.class) { 2859 res.addAll((List<T>) codeSystems.getVersionList(url)); 2860 } else if (class_ == NamingSystem.class) { 2861 res.addAll((List<T>) systems.getVersionList(url)); 2862 } else if (class_ == ActorDefinition.class) { 2863 res.addAll((List<T>) actors.getVersionList(url)); 2864 } else if (class_ == Requirements.class) { 2865 res.addAll((List<T>) requirements.getVersionList(url)); 2866 } else if (class_ == ConceptMap.class) { 2867 res.addAll((List<T>) maps.getVersionList(url)); 2868 } else if (class_ == PlanDefinition.class) { 2869 res.addAll((List<T>) plans.getVersionList(url)); 2870 } else if (class_ == OperationDefinition.class) { 2871 res.addAll((List<T>) operations.getVersionList(url)); 2872 } else if (class_ == Questionnaire.class) { 2873 res.addAll((List<T>) questionnaires.getVersionList(url)); 2874 } else if (class_ == SearchParameter.class) { 2875 res.addAll((List<T>) searchParameters.getVersionList(url)); 2876 } 2877 } 2878 return res; 2879 } 2880 2881 private Set<String> notCanonical = new HashSet<String>(); 2882 2883 protected IWorkerContextManager.IPackageLoadingTracker packageTracker; 2884 private boolean forPublication; 2885 private boolean cachingAllowed = true; 2886 private static boolean nsFailHasFailed; 2887 2888 public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) { 2889 return fetchResourceById(type, uri); 2890 } 2891 2892 @Override 2893 public Resource fetchResourceById(String type, String uri) { 2894 synchronized (lock) { 2895 String[] parts = uri.split("\\/"); 2896 if (!Utilities.noString(type) && parts.length == 1) { 2897 if (allResourcesById.containsKey(type)) { 2898 ResourceProxy res = allResourcesById.get(type).get(parts[0]); 2899 return res == null ? null : res.getResource(); 2900 } else { 2901 return null; 2902 } 2903 } 2904 if (parts.length >= 2) { 2905 if (!Utilities.noString(type)) { 2906 if (!type.equals(parts[parts.length-2])) { 2907 throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri)); 2908 } 2909 } 2910 return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource(); 2911 } else { 2912 throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri)); 2913 } 2914 } 2915 } 2916 2917 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version, Resource sourceForReference) { 2918 try { 2919 return fetchResourceWithException(class_, uri, version, sourceForReference); 2920 } catch (FHIRException e) { 2921 throw new Error(e); 2922 } 2923 } 2924 2925 2926 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 2927 try { 2928 return fetchResourceWithException(class_, uri, null, null); 2929 } catch (FHIRException e) { 2930 throw new Error(e); 2931 } 2932 } 2933 2934 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) { 2935 try { 2936 return fetchResourceWithExceptionByVersion(class_, uri, version, null); 2937 } catch (FHIRException e) { 2938 throw new Error(e); 2939 } 2940 } 2941 2942 @Override 2943 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 2944 try { 2945 return fetchResourceWithException(class_, uri) != null; 2946 } catch (Exception e) { 2947 return false; 2948 } 2949 } 2950 2951 public <T extends Resource> boolean hasResource(String cls, String uri) { 2952 try { 2953 return fetchResourceWithException(cls, uri) != null; 2954 } catch (Exception e) { 2955 return false; 2956 } 2957 } 2958 2959 public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version) { 2960 try { 2961 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2962 } catch (Exception e) { 2963 return false; 2964 } 2965 } 2966 2967 public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version) { 2968 try { 2969 return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null; 2970 } catch (Exception e) { 2971 return false; 2972 } 2973 } 2974 2975 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, String version, Resource sourceForReference) { 2976 try { 2977 return fetchResourceWithExceptionByVersion(class_, uri, version, sourceForReference) != null; 2978 } catch (Exception e) { 2979 return false; 2980 } 2981 } 2982 2983 public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version, FhirPublication fhirVersion) { 2984 try { 2985 return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null; 2986 } catch (Exception e) { 2987 return false; 2988 } 2989 } 2990 2991 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, Resource sourceOfReference) { 2992 try { 2993 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2994 } catch (Exception e) { 2995 return false; 2996 } 2997 } 2998 2999 public void reportStatus(JsonObject json) { 3000 synchronized (lock) { 3001 json.addProperty("codeystem-count", codeSystems.size()); 3002 json.addProperty("valueset-count", valueSets.size()); 3003 json.addProperty("conceptmap-count", maps.size()); 3004 json.addProperty("transforms-count", transforms.size()); 3005 json.addProperty("structures-count", structures.size()); 3006 json.addProperty("guides-count", guides.size()); 3007 json.addProperty("statements-count", capstmts.size()); 3008 json.addProperty("measures-count", measures.size()); 3009 json.addProperty("libraries-count", libraries.size()); 3010 } 3011 } 3012 3013 3014 public void dropResource(Resource r) throws FHIRException { 3015 dropResource(r.fhirType(), r.getId()); 3016 } 3017 3018 public void dropResource(String fhirType, String id) { 3019 synchronized (lock) { 3020 3021 Map<String, ResourceProxy> map = allResourcesById.get(fhirType); 3022 if (map == null) { 3023 map = new HashMap<String, ResourceProxy>(); 3024 allResourcesById.put(fhirType, map); 3025 } 3026 if (map.containsKey(id)) { 3027 map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions) 3028 } 3029 3030 if (fhirType.equals("StructureDefinition")) { 3031 structures.drop(id); 3032 typeManager.reload(); 3033 } else if (fhirType.equals("ImplementationGuide")) { 3034 guides.drop(id); 3035 } else if (fhirType.equals("CapabilityStatement")) { 3036 capstmts.drop(id); 3037 } else if (fhirType.equals("Measure")) { 3038 measures.drop(id); 3039 } else if (fhirType.equals("Library")) { 3040 libraries.drop(id); 3041 } else if (fhirType.equals("ValueSet")) { 3042 valueSets.drop(id); 3043 } else if (fhirType.equals("CodeSystem")) { 3044 codeSystems.drop(id); 3045 } else if (fhirType.equals("OperationDefinition")) { 3046 operations.drop(id); 3047 } else if (fhirType.equals("Questionnaire")) { 3048 questionnaires.drop(id); 3049 } else if (fhirType.equals("ConceptMap")) { 3050 maps.drop(id); 3051 } else if (fhirType.equals("StructureMap")) { 3052 transforms.drop(id); 3053 } else if (fhirType.equals("NamingSystem")) { 3054 systems.drop(id); 3055 systemUrlMap = null; 3056 } else if (fhirType.equals("ActorDefinition")) { 3057 actors.drop(id); 3058 } else if (fhirType.equals("Requirements")) { 3059 requirements.drop(id); 3060 } 3061 } 3062 } 3063 3064 private <T extends CanonicalResource> void dropMetadataResource(Map<String, T> map, String id) { 3065 T res = map.get(id); 3066 if (res != null) { 3067 map.remove(id); 3068 if (map.containsKey(res.getUrl())) { 3069 map.remove(res.getUrl()); 3070 } 3071 if (res.getVersion() != null) { 3072 if (map.containsKey(res.getUrl()+"|"+res.getVersion())) { 3073 map.remove(res.getUrl()+"|"+res.getVersion()); 3074 } 3075 } 3076 } 3077 } 3078 3079 3080 public String listSupportedSystems() { 3081 synchronized (lock) { 3082 String sl = null; 3083 for (String s : supportedCodeSystems.keySet()) { 3084 SystemSupportInformation ss = supportedCodeSystems.get(s); 3085 if (ss.isSupported()) { 3086 sl = sl == null ? s : sl + "\r\n" + s; 3087 } 3088 } 3089 return sl; 3090 } 3091 } 3092 3093 3094 public int totalCount() { 3095 synchronized (lock) { 3096 return valueSets.size() + maps.size() + structures.size() + transforms.size(); 3097 } 3098 } 3099 3100 public List<ConceptMap> listMaps() { 3101 List<ConceptMap> m = new ArrayList<ConceptMap>(); 3102 synchronized (lock) { 3103 maps.listAll(m); 3104 } 3105 return m; 3106 } 3107 3108 public List<StructureDefinition> listStructures() { 3109 List<StructureDefinition> m = new ArrayList<StructureDefinition>(); 3110 synchronized (lock) { 3111 structures.listAll(m); 3112 } 3113 return m; 3114 } 3115 3116 public List<ValueSet> listValueSets() { 3117 List<ValueSet> m = new ArrayList<ValueSet>(); 3118 synchronized (lock) { 3119 valueSets.listAll(m); 3120 } 3121 return m; 3122 } 3123 3124 public List<CodeSystem> listCodeSystems() { 3125 List<CodeSystem> m = new ArrayList<CodeSystem>(); 3126 synchronized (lock) { 3127 codeSystems.listAll(m); 3128 } 3129 return m; 3130 } 3131 3132 public StructureDefinition getStructure(String code) { 3133 synchronized (lock) { 3134 return structures.get(code); 3135 } 3136 } 3137 3138 private String getUri(NamingSystem ns) { 3139 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 3140 if (id.getType() == NamingSystemIdentifierType.URI) { 3141 return id.getValue(); 3142 } 3143 } 3144 return null; 3145 } 3146 3147 private boolean hasOid(NamingSystem ns, String oid) { 3148 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 3149 if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid)) { 3150 return true; 3151 } 3152 } 3153 return false; 3154 } 3155 3156 public void cacheVS(JsonObject json, Map<String, ValidationResult> t) { 3157 synchronized (lock) { 3158 validationCache.put(json.get("url").getAsString(), t); 3159 } 3160 } 3161 3162 public SearchParameter getSearchParameter(String code) { 3163 synchronized (lock) { 3164 return searchParameters.get(code); 3165 } 3166 } 3167 3168 @Override 3169 public org.hl7.fhir.r5.context.ILoggingService getLogger() { 3170 return logger; 3171 } 3172 3173 3174 public StructureDefinition fetchTypeDefinition(String typeName, FhirPublication fhirVersion) { 3175 return fetchTypeDefinition(typeName); 3176 } 3177 3178 @Override 3179 public StructureDefinition fetchTypeDefinition(String typeName) { 3180 if (Utilities.isAbsoluteUrl(typeName)) { 3181 StructureDefinition res = fetchResource(StructureDefinition.class, typeName); 3182 if (res != null) { 3183 return res; 3184 } 3185 } 3186 StructureDefinition structureDefinition = typeManager.fetchTypeDefinition(typeName); 3187 generateSnapshot(structureDefinition, "5"); 3188 return structureDefinition; 3189 } 3190 3191 void generateSnapshot(StructureDefinition structureDefinition, String breadcrumb) { 3192 if (structureDefinition != null && !structureDefinition.isGeneratedSnapshot()) { 3193 if (structureDefinition.isGeneratingSnapshot()) { 3194 throw new FHIRException("Attempt to fetch the profile "+ structureDefinition.getVersionedUrl()+" while generating the snapshot for it"); 3195 } 3196 try { 3197 3198 // logger.logDebugMessage(LogCategory.GENERATE,"Generating snapshot for "+ structureDefinition.getVersionedUrl()); 3199 3200 // structureDefinition.setGeneratingSnapshot(true); 3201 try { 3202 cutils.generateSnapshot(structureDefinition); 3203 } finally { 3204 //structureDefinition.setGeneratingSnapshot(false); 3205 } 3206 } catch (Exception e) { 3207 // not sure what to do in this case? 3208 log.error("Unable to generate snapshot in @" + breadcrumb + " for " + structureDefinition.getVersionedUrl()+": "+e.getMessage()); 3209 logger.logDebugMessage(ILoggingService.LogCategory.GENERATE, ExceptionUtils.getStackTrace(e)); 3210 } 3211 } 3212 } 3213 3214 @Override 3215 public List<StructureDefinition> fetchTypeDefinitions(String typeName) { 3216 return typeManager.getDefinitions(typeName); 3217 } 3218 3219 public boolean isPrimitiveType(String type) { 3220 return typeManager.isPrimitive(type); 3221 } 3222 3223 public boolean isDataType(String type) { 3224 return typeManager.isDataType(type); 3225 } 3226 3227 public boolean isTlogging() { 3228 return tlogging; 3229 } 3230 3231 public void setTlogging(boolean tlogging) { 3232 this.tlogging = tlogging; 3233 } 3234 3235 public UcumService getUcumService() { 3236 return ucumService; 3237 } 3238 3239 public void setUcumService(UcumService ucumService) { 3240 this.ucumService = ucumService; 3241 } 3242 3243 public String getLinkForUrl(String corePath, String url) { 3244 if (url == null) { 3245 return null; 3246 } 3247 3248 if (codeSystems.has(url)) { 3249 return codeSystems.get(url).getWebPath(); 3250 } 3251 3252 if (valueSets.has(url)) { 3253 return valueSets.get(url).getWebPath(); 3254 } 3255 3256 if (maps.has(url)) { 3257 return maps.get(url).getWebPath(); 3258 } 3259 3260 if (transforms.has(url)) { 3261 return transforms.get(url).getWebPath(); 3262 } 3263 3264 if (actors.has(url)) { 3265 return actors.get(url).getWebPath(); 3266 } 3267 3268 if (requirements.has(url)) { 3269 return requirements.get(url).getWebPath(); 3270 } 3271 3272 if (structures.has(url)) { 3273 return structures.get(url).getWebPath(); 3274 } 3275 3276 if (guides.has(url)) { 3277 return guides.get(url).getWebPath(); 3278 } 3279 3280 if (capstmts.has(url)) { 3281 return capstmts.get(url).getWebPath(); 3282 } 3283 3284 if (measures.has(url)) { 3285 return measures.get(url).getWebPath(); 3286 } 3287 3288 if (libraries.has(url)) { 3289 return libraries.get(url).getWebPath(); 3290 } 3291 3292 if (searchParameters.has(url)) { 3293 return searchParameters.get(url).getWebPath(); 3294 } 3295 3296 if (questionnaires.has(url)) { 3297 return questionnaires.get(url).getWebPath(); 3298 } 3299 3300 if (operations.has(url)) { 3301 return operations.get(url).getWebPath(); 3302 } 3303 3304 if (plans.has(url)) { 3305 return plans.get(url).getWebPath(); 3306 } 3307 3308 if (url.equals("http://loinc.org")) { 3309 return corePath+"loinc.html"; 3310 } 3311 if (url.equals("http://unitsofmeasure.org")) { 3312 return corePath+"ucum.html"; 3313 } 3314 if (url.equals("http://snomed.info/sct")) { 3315 return corePath+"snomed.html"; 3316 } 3317 return null; 3318 } 3319 3320 public List<ImplementationGuide> allImplementationGuides() { 3321 List<ImplementationGuide> res = new ArrayList<>(); 3322 guides.listAll(res); 3323 return res; 3324 } 3325 3326 @Override 3327 public Set<String> getBinaryKeysAsSet() { return binaries.keySet(); } 3328 3329 @Override 3330 public boolean hasBinaryKey(String binaryKey) { 3331 return binaries.containsKey(binaryKey); 3332 } 3333 3334 @Override 3335 public byte[] getBinaryForKey(String binaryKey) { 3336 IByteProvider bp = binaries.get(binaryKey); 3337 try { 3338 return bp == null ? null : bp.bytes(); 3339 } catch (Exception e) { 3340 throw new FHIRException(e); 3341 } 3342 } 3343 3344 public void finishLoading(boolean genSnapshots) { 3345 if (!hasResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Base")) { 3346 cacheResource(ProfileUtilities.makeBaseDefinition(version)); 3347 } 3348 new CoreVersionPinner(this).pinCoreVersions(listCodeSystems(), listValueSets(), listStructures()); 3349 if(genSnapshots) { 3350 for (StructureDefinition sd : listStructures()) { 3351 try { 3352 if (sd.getSnapshot().isEmpty()) { 3353 new ContextUtilities(this).generateSnapshot(sd); 3354 // new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd); 3355 } 3356 } catch (Exception e) { 3357 log.error("Unable to generate snapshot @1 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage()); 3358 logger.logDebugMessage(LogCategory.GENERATE, ExceptionUtils.getStackTrace(e)); 3359 } 3360 } 3361 } 3362 3363 codeSystems.setVersion(version); 3364 valueSets.setVersion(version); 3365 maps.setVersion(version); 3366 transforms.setVersion(version); 3367 structures.setVersion(version); 3368 typeManager.reload(); 3369 measures.setVersion(version); 3370 libraries.setVersion(version); 3371 guides.setVersion(version); 3372 capstmts.setVersion(version); 3373 searchParameters.setVersion(version); 3374 questionnaires.setVersion(version); 3375 operations.setVersion(version); 3376 plans.setVersion(version); 3377 systems.setVersion(version); 3378 actors.setVersion(version); 3379 requirements.setVersion(version); 3380 } 3381 3382 protected String tail(String url) { 3383 if (Utilities.noString(url)) { 3384 return "noname"; 3385 } 3386 if (url.contains("/")) { 3387 return url.substring(url.lastIndexOf("/")+1); 3388 } 3389 return url; 3390 } 3391 3392 public int getClientRetryCount() { 3393 return terminologyClientManager.getRetryCount(); 3394 } 3395 3396 public IWorkerContext setClientRetryCount(int value) { 3397 terminologyClientManager.setRetryCount(value); 3398 return this; 3399 } 3400 3401 public TerminologyClientManager getTxClientManager() { 3402 return terminologyClientManager; 3403 } 3404 3405 public String getCacheId() { 3406 return terminologyClientManager.getCacheId(); 3407 } 3408 3409 public TimeTracker clock() { 3410 return clock; 3411 } 3412 3413 public int countAllCaches() { 3414 return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() + 3415 guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() + 3416 systems.size()+ actors.size()+ requirements.size(); 3417 } 3418 3419 public Set<String> getCodeSystemsUsed() { 3420 return codeSystemsUsed ; 3421 } 3422 3423 public IWorkerContextManager.ICanonicalResourceLocator getLocator() { 3424 return locator; 3425 } 3426 3427 public void setLocator(IWorkerContextManager.ICanonicalResourceLocator locator) { 3428 this.locator = locator; 3429 } 3430 3431 public String getUserAgent() { 3432 return userAgent; 3433 } 3434 3435 protected void setUserAgent(String userAgent) { 3436 this.userAgent = userAgent; 3437 terminologyClientManager.setUserAgent(userAgent); 3438 } 3439 3440 3441 public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() { 3442 return packageTracker; 3443 } 3444 3445 public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker packageTracker) { 3446 this.packageTracker = packageTracker; 3447 return this; 3448 } 3449 3450 3451 @Override 3452 public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps) { 3453 // TODO Auto-generated method stub 3454 return new PEBuilder(this, elementProps, fixedProps); 3455 } 3456 3457 public boolean isForPublication() { 3458 return forPublication; 3459 } 3460 3461 public void setForPublication(boolean value) { 3462 forPublication = value; 3463 } 3464 3465 public boolean isCachingAllowed() { 3466 return cachingAllowed; 3467 } 3468 3469 public void setCachingAllowed(boolean cachingAllowed) { 3470 this.cachingAllowed = cachingAllowed; 3471 } 3472 3473 @Override 3474 public IOIDServices.OIDSummary urlsForOid(String oid, String resourceType) { 3475 IOIDServices.OIDSummary set = urlsForOid(oid, resourceType, true); 3476 if (set.getDefinitions().size() > 1) { 3477 set = urlsForOid(oid, resourceType, false); 3478 } 3479 return set; 3480 } 3481 3482 public IOIDServices.OIDSummary urlsForOid(String oid, String resourceType, boolean retired) { 3483 IOIDServices.OIDSummary summary = new IOIDServices.OIDSummary(); 3484 if (oid != null) { 3485 if (oidCacheManual.containsKey(oid)) { 3486 summary.addOIDs(oidCacheManual.get(oid)); 3487 } 3488 for (OIDSource os : oidSources) { 3489 if (os.db == null) { 3490 os.db = connectToOidSource(os.folder); 3491 } 3492 if (os.db != null) { 3493 try { 3494 PreparedStatement psql = resourceType == null ? 3495 os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where OID = ?") : 3496 os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where TYPE = '"+resourceType+"' and OID = ?"); 3497 psql.setString(1, oid); 3498 ResultSet rs = psql.executeQuery(); 3499 while (rs.next()) { 3500 if (retired || !"retired".equals(rs.getString(4))) { 3501 String rt = rs.getString(1); 3502 String url = rs.getString(2); 3503 String version = rs.getString(3); 3504 String status = rs.getString(4); 3505 summary.addOID(new IOIDServices.OIDDefinition(rt, oid, url, version, os.pid, status)); 3506 } 3507 } 3508 } catch (Exception e) { 3509 // nothing, there would alreagy have been an error 3510 // e.printStackTrace(); 3511 } 3512 } 3513 } 3514 3515 switch (oid) { 3516 case "2.16.840.1.113883.6.1" : 3517 summary.addOID(new IOIDServices.OIDDefinition("CodeSystem", "2.16.840.1.113883.6.1", "http://loinc.org", null, null, null)); 3518 break; 3519 case "2.16.840.1.113883.6.8" : 3520 summary.addOID(new IOIDServices.OIDDefinition("CodeSystem", "2.16.840.1.113883.6.8", "http://unitsofmeasure.org", null, null, null)); 3521 break; 3522 case "2.16.840.1.113883.6.96" : 3523 summary.addOID(new IOIDServices.OIDDefinition("CodeSystem", "2.16.840.1.113883.6.96", "http://snomed.info/sct", null, null, null)); 3524 break; 3525 default: 3526 } 3527 } 3528 summary.sort(); 3529 return summary; 3530 } 3531 3532 private Connection connectToOidSource(String folder) { 3533 try { 3534 File ff = ManagedFileAccess.file(folder); 3535 File of = ManagedFileAccess.file(Utilities.path(ff.getAbsolutePath(), ".oid-map-2.db")); 3536 if (!of.exists()) { 3537 OidIndexBuilder oidBuilder = new OidIndexBuilder(ff, of); 3538 oidBuilder.build(); 3539 } 3540 return DriverManager.getConnection("jdbc:sqlite:"+of.getAbsolutePath()); 3541 } catch (Exception e) { 3542 return null; 3543 } 3544 } 3545 3546 3547 public void unload() { 3548 3549 codeSystems.unload(); 3550 valueSets.unload(); 3551 maps.unload(); 3552 transforms.unload(); 3553 structures.unload(); 3554 typeManager.unload(); 3555 measures.unload(); 3556 libraries.unload(); 3557 guides.unload(); 3558 capstmts.unload(); 3559 searchParameters.unload(); 3560 questionnaires.unload(); 3561 operations.unload(); 3562 plans.unload(); 3563 actors.unload(); 3564 requirements.unload(); 3565 systems.unload(); 3566 3567 binaries.clear(); 3568 validationCache.clear(); 3569 txCache.unload(); 3570} 3571 3572 private <T extends Resource> T doFindTxResource(Class<T> class_, String canonical) { 3573 // well, we haven't found it locally. We're going look it up 3574 if (class_ == ValueSet.class) { 3575 ValueSet ivs = new ImplicitValueSets(getExpansionParameters()).generateImplicitValueSet(canonical); 3576 if (ivs != null) { 3577 return (T) ivs; 3578 } 3579 SourcedValueSet svs = null; 3580 if (txCache.hasValueSet(canonical)) { 3581 svs = txCache.getValueSet(canonical); 3582 } else { 3583 svs = terminologyClientManager.findValueSetOnServer(canonical); 3584 txCache.cacheValueSet(canonical, svs); 3585 } 3586 if (svs != null) { 3587 String web = ExtensionUtilities.readStringExtension(svs.getVs(), ExtensionDefinitions.EXT_WEB_SOURCE_OLD, ExtensionDefinitions.EXT_WEB_SOURCE_NEW); 3588 if (web == null) { 3589 web = Utilities.pathURL(svs.getServer(), "ValueSet", svs.getVs().getIdBase()); 3590 } 3591 svs.getVs().setWebPath(web); 3592 svs.getVs().setUserData(UserDataNames.render_external_link, svs.getServer()); // so we can render it differently 3593 } 3594 if (svs == null) { 3595 return null; 3596 } else { 3597 cacheResource(svs.getVs()); 3598 return (T) svs.getVs(); 3599 } 3600 } else if (class_ == CodeSystem.class) { 3601 SourcedCodeSystem scs = null; 3602 if (txCache.hasCodeSystem(canonical)) { 3603 scs = txCache.getCodeSystem(canonical); 3604 } else { 3605 scs = terminologyClientManager.findCodeSystemOnServer(canonical); 3606 txCache.cacheCodeSystem(canonical, scs); 3607 } 3608 if (scs != null) { 3609 String web = ExtensionUtilities.readStringExtension(scs.getCs(), ExtensionDefinitions.EXT_WEB_SOURCE_OLD, ExtensionDefinitions.EXT_WEB_SOURCE_NEW); 3610 if (web == null) { 3611 web = Utilities.pathURL(scs.getServer(), "ValueSet", scs.getCs().getIdBase()); 3612 } 3613 scs.getCs().setWebPath(web); 3614 scs.getCs().setUserData(UserDataNames.render_external_link, scs.getServer()); // so we can render it differently 3615 } 3616 if (scs == null) { 3617 return null; 3618 } else { 3619 cacheResource(scs.getCs()); 3620 return (T) scs.getCs(); 3621 } 3622 } else { 3623 throw new Error("Not supported: doFindTxResource with type of "+class_.getName()); 3624 } 3625 } 3626 3627 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version, Resource sourceOfReference) { 3628 if (canonical == null) { 3629 return null; 3630 } 3631 T result = fetchResource(class_, canonical, version, sourceOfReference); 3632 if (result == null) { 3633 result = doFindTxResource(class_, canonical); 3634 } 3635 return result; 3636 } 3637 3638 public <T extends Resource> T findTxResource(Class<T> class_, String canonical) { 3639 if (canonical == null) { 3640 return null; 3641 } 3642 T result = fetchResource(class_, canonical); 3643 if (result == null) { 3644 result = doFindTxResource(class_, canonical); 3645 } 3646 return result; 3647 } 3648 3649 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version) { 3650 if (canonical == null) { 3651 return null; 3652 } 3653 T result = fetchResource(class_, canonical, version); 3654 if (result == null) { 3655 result = doFindTxResource(class_, canonical+"|"+version); 3656 } 3657 return result; 3658 } 3659 3660 @Override 3661 public <T extends Resource> List<T> fetchResourceVersions(Class<T> class_, String uri) { 3662 List<T> res = new ArrayList<>(); 3663 if (uri != null && !uri.startsWith("#")) { 3664 if (class_ == StructureDefinition.class) { 3665 uri = ProfileUtilities.sdNs(uri, null); 3666 } 3667 if (uri.contains("|")) { 3668 throw new Error("at fetchResourceVersions, but a version is found in the uri - should not happen ('"+uri+"')"); 3669 } 3670 if (uri.contains("#")) { 3671 uri = uri.substring(0, uri.indexOf("#")); 3672 } 3673 synchronized (lock) { 3674 if (class_ == Resource.class || class_ == null) { 3675 List<ResourceProxy> list = allResourcesByUrl.get(uri); 3676 if (list != null) { 3677 for (ResourceProxy r : list) { 3678 if (uri.equals(r.getUrl())) { 3679 res.add((T) r.getResource()); 3680 } 3681 } 3682 } 3683 } 3684 if (class_ == ImplementationGuide.class || class_ == Resource.class || class_ == null) { 3685 for (ImplementationGuide cr : guides.getForUrl(uri)) { 3686 res.add((T) cr); 3687 } 3688 } else if (class_ == CapabilityStatement.class || class_ == Resource.class || class_ == null) { 3689 for (CapabilityStatement cr : capstmts.getForUrl(uri)) { 3690 res.add((T) cr); 3691 } 3692 } else if (class_ == Measure.class || class_ == Resource.class || class_ == null) { 3693 for (Measure cr : measures.getForUrl(uri)) { 3694 res.add((T) cr); 3695 } 3696 } else if (class_ == Library.class || class_ == Resource.class || class_ == null) { 3697 for (Library cr : libraries.getForUrl(uri)) { 3698 res.add((T) cr); 3699 } 3700 } else if (class_ == StructureDefinition.class || class_ == Resource.class || class_ == null) { 3701 for (StructureDefinition cr : structures.getForUrl(uri)) { 3702 res.add((T) cr); 3703 } 3704 } else if (class_ == StructureMap.class || class_ == Resource.class || class_ == null) { 3705 for (StructureMap cr : transforms.getForUrl(uri)) { 3706 res.add((T) cr); 3707 } 3708 } else if (class_ == NamingSystem.class || class_ == Resource.class || class_ == null) { 3709 for (NamingSystem cr : systems.getForUrl(uri)) { 3710 res.add((T) cr); 3711 } 3712 } else if (class_ == ValueSet.class || class_ == Resource.class || class_ == null) { 3713 for (ValueSet cr : valueSets.getForUrl(uri)) { 3714 res.add((T) cr); 3715 } 3716 } else if (class_ == CodeSystem.class || class_ == Resource.class || class_ == null) { 3717 for (CodeSystem cr : codeSystems.getForUrl(uri)) { 3718 res.add((T) cr); 3719 } 3720 } else if (class_ == ConceptMap.class || class_ == Resource.class || class_ == null) { 3721 for (ConceptMap cr : maps.getForUrl(uri)) { 3722 res.add((T) cr); 3723 } 3724 } else if (class_ == ActorDefinition.class || class_ == Resource.class || class_ == null) { 3725 for (ActorDefinition cr : actors.getForUrl(uri)) { 3726 res.add((T) cr); 3727 } 3728 } else if (class_ == Requirements.class || class_ == Resource.class || class_ == null) { 3729 for (Requirements cr : requirements.getForUrl(uri)) { 3730 res.add((T) cr); 3731 } 3732 } else if (class_ == PlanDefinition.class || class_ == Resource.class || class_ == null) { 3733 for (PlanDefinition cr : plans.getForUrl(uri)) { 3734 res.add((T) cr); 3735 } 3736 } else if (class_ == OperationDefinition.class || class_ == Resource.class || class_ == null) { 3737 for (OperationDefinition cr : operations.getForUrl(uri)) { 3738 res.add((T) cr); 3739 } 3740 } else if (class_ == Questionnaire.class || class_ == Resource.class || class_ == null) { 3741 for (Questionnaire cr : questionnaires.getForUrl(uri)) { 3742 res.add((T) cr); 3743 } 3744 } else if (class_ == SearchParameter.class || class_ == Resource.class || class_ == null) { 3745 for (SearchParameter cr : searchParameters.getForUrl(uri)) { 3746 res.add((T) cr); 3747 } 3748 } 3749 } 3750 } 3751 return res; 3752 } 3753 3754 public void setLocale(Locale locale) { 3755 super.setLocale(locale); 3756 if (locale == null) { 3757 return; 3758 } 3759 final String languageTag = locale.toLanguageTag(); 3760 if ("und".equals(languageTag)) { 3761 throw new FHIRException("The locale " + locale.toString() + " is not valid"); 3762 } 3763 3764 if (expansionParameters.get() == null) { 3765 return; 3766 } 3767 3768 /* 3769 If displayLanguage is an existing parameter, we check to see if it was added automatically or explicitly set by 3770 the user 3771 3772 * If it was added automatically, we update it to the new locale 3773 * If it was set by the user, we do not update it. 3774 3775 In both cases, we are done. 3776 */ 3777 3778 Parameters newExpansionParameters = copyExpansionParametersWithUserData(); 3779 3780 int displayLanguageCount = 0; 3781 for (ParametersParameterComponent expParameter : newExpansionParameters.getParameter()) { 3782 if ("displayLanguage".equals(expParameter.getName())) { 3783 if (expParameter.hasUserData(UserDataNames.auto_added_parameter)) { 3784 expParameter.setValue(new CodeType(languageTag)); 3785 } 3786 displayLanguageCount++; 3787 } 3788 } 3789 if (displayLanguageCount > 1) { 3790 throw new FHIRException("Multiple displayLanguage parameters found"); 3791 } 3792 if (displayLanguageCount == 1) { 3793 this.expansionParameters.set(newExpansionParameters); 3794 return; 3795 } 3796 3797 // There is no displayLanguage parameter so we are free to add a "defaultDisplayLanguage" instead. 3798 3799 int defaultDisplayLanguageCount = 0; 3800 for (ParametersParameterComponent expansionParameter : newExpansionParameters.getParameter()) { 3801 if ("defaultDisplayLanguage".equals(expansionParameter.getName())) { 3802 expansionParameter.setValue(new CodeType(languageTag)); 3803 expansionParameter.setUserData(UserDataNames.auto_added_parameter, true); 3804 defaultDisplayLanguageCount++; 3805 } 3806 } 3807 if (defaultDisplayLanguageCount > 1) { 3808 throw new FHIRException("Multiple defaultDisplayLanguage parameters found"); 3809 } 3810 if (defaultDisplayLanguageCount == 0) { 3811 ParametersParameterComponent p = newExpansionParameters.addParameter(); 3812 p.setName("defaultDisplayLanguage"); 3813 p.setValue(new CodeType(languageTag)); 3814 p.setUserData(UserDataNames.auto_added_parameter, true); 3815 } 3816 this.expansionParameters.set(newExpansionParameters); 3817 } 3818 3819 private Parameters copyExpansionParametersWithUserData() { 3820 Parameters newExpansionParameters = new Parameters(); 3821 // Copy all existing parameters include userData (not usually included in copyies) 3822 if (this.expansionParameters.get() != null && this.expansionParameters.get().hasParameter()) { 3823 for (ParametersParameterComponent expParameter : this.expansionParameters.get().getParameter()) { 3824 ParametersParameterComponent copy = newExpansionParameters.addParameter(); 3825 expParameter.copyValues(copy); 3826 copy.copyUserData(expParameter); 3827 } 3828 } 3829 return newExpansionParameters; 3830 } 3831 3832 @Override 3833 public OperationOutcome validateTxResource(ValidationOptions options, Resource resource) { 3834 if (resource instanceof ValueSet) { 3835 ValueSet vs = (ValueSet) resource; 3836 Set<String> systems = findRelevantSystems(vs); 3837 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false); 3838 if (tc == null) { 3839 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 3840 } 3841 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 3842 codeSystemsUsed.add(inc.getSystem()); 3843 } 3844 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 3845 codeSystemsUsed.add(inc.getSystem()); 3846 } 3847 3848 txLog("$validate ValueSet on "+tc.getAddress()); 3849 if (txLog != null) { 3850 txLog.clearLastId(); 3851 } 3852 return tc.getClient().validateResource(vs); 3853 } 3854 return null; 3855 } 3856 3857 3858 public List<String> getSuppressedMappings() { 3859 return suppressedMappings; 3860 } 3861 3862 public void setSuppressedMappings(List<String> suppressedMappings) { 3863 this.suppressedMappings = suppressedMappings; 3864 this.cutils.setSuppressedMappings(suppressedMappings); 3865 } 3866 3867 public ContextUtilities getCutils() { 3868 return cutils; 3869 } 3870 3871 3872 public String txCacheReport() { 3873 return txCache.getReport(); 3874 } 3875 3876 3877 public static boolean isAllowedToIterateTerminologyResources() { 3878 return allowedToIterateTerminologyResources; 3879 } 3880 3881 public static void setAllowedToIterateTerminologyResources(boolean allowedToIterateTerminologyResources) { 3882 BaseWorkerContext.allowedToIterateTerminologyResources = allowedToIterateTerminologyResources; 3883 } 3884 3885 public IOIDServices oidServices() { 3886 return this; 3887 } 3888 3889 public IWorkerContextManager getManager() { 3890 return this; 3891 } 3892}