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