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