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