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