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