![](/hapi-fhir/images/logos/raccoon-forwards.png)
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 codeSystemsUsed.add(inc.getSystem()); 938 } 939 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 940 codeSystemsUsed.add(inc.getSystem()); 941 } 942 943 CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical); 944 ValueSetExpansionOutcome res; 945 if (cacheOk) { 946 res = txCache.getExpansion(cacheToken); 947 if (res != null) { 948 return res; 949 } 950 } 951 952 if (!noLimits) { 953 p.addParameter("count", expandCodesLimit); 954 p.addParameter("offset", 0); 955 } 956 p.setParameter("excludeNested", !hierarchical); 957 if (incompleteOk) { 958 p.setParameter("incomplete-ok", true); 959 } 960 961 List<String> allErrors = new ArrayList<>(); 962 963 // ok, first we try to expand locally 964 ValueSetExpander vse = constructValueSetExpanderSimple(new ValidationOptions(vs.getFHIRPublicationVersion())); 965 res = null; 966 try { 967 res = vse.expand(vs, p); 968 } catch (Exception e) { 969 allErrors.addAll(vse.getAllErrors()); 970 e.printStackTrace(); 971 res = new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, e instanceof EFhirClientException); 972 } 973 allErrors.addAll(vse.getAllErrors()); 974 if (res.getValueset() != null) { 975 if (!res.getValueset().hasUrl()) { 976 throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET)); 977 } 978 txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT); 979 return res; 980 } 981 if (res.getErrorClass() == TerminologyServiceErrorClass.INTERNAL_ERROR || isNoTerminologyServer()) { // this class is created specifically to say: don't consult the server 982 return new ValueSetExpansionOutcome(res.getError(), res.getErrorClass(), false); 983 } 984 985 // if that failed, we try to expand on the server 986 if (noTerminologyServer) { 987 return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, allErrors, false); 988 } 989 990 p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); 991 Set<String> systems = findRelevantSystems(vs); 992 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, true); 993 addDependentResources(tc, p, vs); 994 995 996 txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress()); 997 998 try { 999 ValueSet result = tc.getClient().expandValueset(vs, p); 1000 if (result != null) { 1001 if (!result.hasUrl()) { 1002 result.setUrl(vs.getUrl()); 1003 } 1004 if (!result.hasUrl()) { 1005 throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2)); 1006 } 1007 } 1008 res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId()); 1009 } catch (Exception e) { 1010 if (res != null && !res.isFromServer()) { 1011 res = new ValueSetExpansionOutcome(res.getError()+" (and "+e.getMessage()+")", res.getErrorClass(), false); 1012 } else { 1013 res = new ValueSetExpansionOutcome((e.getMessage() == null ? e.getClass().getName() : e.getMessage()), TerminologyServiceErrorClass.UNKNOWN, allErrors, true).setTxLink(txLog == null ? null : txLog.getLastId()); 1014 } 1015 } 1016 txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT); 1017 return res; 1018 } 1019 1020// private boolean hasTooCostlyExpansion(ValueSet valueset) { 1021// return valueset != null && valueset.hasExpansion() && ToolingExtensions.hasExtension(valueset.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY); 1022// } 1023 1024 // --- validate code ------------------------------------------------------------------------------- 1025 1026 @Override 1027 public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display) { 1028 assert options != null; 1029 Coding c = new Coding(system, version, code, display); 1030 return validateCode(options, c, null); 1031 } 1032 1033 @Override 1034 public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display, ValueSet vs) { 1035 assert options != null; 1036 Coding c = new Coding(system, version, code, display); 1037 ValidationResult ret = validateCode(options, "$", c, vs); 1038 ret.trimPath("$"); 1039 return ret; 1040 } 1041 1042 @Override 1043 public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) { 1044 assert options != null; 1045 Coding c = new Coding(null, code, null); 1046 return validateCode(options.withGuessSystem(), c, vs); 1047 } 1048 1049 1050 @Override 1051 public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) { 1052 if (options == null) { 1053 options = ValidationOptions.defaults(); 1054 } 1055 // 1st pass: what is in the cache? 1056 // 2nd pass: What can we do internally 1057 // 3rd pass: hit the server 1058 for (CodingValidationRequest t : codes) { 1059 t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs == null ? t.getVsObj() : vs, expParameters) : null); 1060 if (t.getCoding().hasSystem()) { 1061 codeSystemsUsed.add(t.getCoding().getSystem()); 1062 } 1063 if (txCache != null) { 1064 t.setResult(txCache.getValidation(t.getCacheToken())); 1065 } 1066 } 1067 if (options.isUseClient()) { 1068 for (CodingValidationRequest t : codes) { 1069 if (!t.hasResult()) { 1070 try { 1071 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs == null ? t.getVsObj() : vs); 1072 vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient()); 1073 ValidationResult res = vsc.validateCode("Coding", t.getCoding()); 1074 if (txCache != null) { 1075 txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT); 1076 } 1077 t.setResult(res); 1078 } catch (Exception e) { 1079 } 1080 } 1081 } 1082 } 1083 1084 for (CodingValidationRequest t : codes) { 1085 if (!t.hasResult()) { 1086 String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem(); 1087 if (!options.isUseServer()) { 1088 t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null)); 1089 } else if (unsupportedCodeSystems.contains(codeKey)) { 1090 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null)); 1091 } else if (noTerminologyServer) { 1092 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null)); 1093 } 1094 } 1095 } 1096 1097 if (expParameters == null) 1098 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1099 // for those that that failed, we try to validate on the server 1100 Bundle batch = new Bundle(); 1101 batch.setType(BundleType.BATCH); 1102 Set<String> systems = findRelevantSystems(vs); 1103 for (CodingValidationRequest codingValidationRequest : codes) { 1104 if (!codingValidationRequest.hasResult()) { 1105 Parameters pIn = constructParameters(options, codingValidationRequest, vs == null ? codingValidationRequest.getVsObj() : vs); 1106 setTerminologyOptions(options, pIn); 1107 BundleEntryComponent be = batch.addEntry(); 1108 be.setResource(pIn); 1109 be.getRequest().setMethod(HTTPVerb.POST); 1110 if (vs != null || codingValidationRequest.getVsObj() != null) { 1111 be.getRequest().setUrl("ValueSet/$validate-code"); 1112 } else { 1113 be.getRequest().setUrl("CodeSystem/$validate-code"); 1114 } 1115 be.setUserData("source", codingValidationRequest); 1116 systems.add(codingValidationRequest.getCoding().getSystem()); 1117 findRelevantSystems(systems, codingValidationRequest.getCoding()); 1118 } 1119 } 1120 1121 if (batch.getEntry().size() > 0) { 1122 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false); 1123 Bundle resp = processBatch(tc, batch, systems); 1124 for (int i = 0; i < batch.getEntry().size(); i++) { 1125 CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); 1126 BundleEntryComponent r = resp.getEntry().get(i); 1127 1128 if (r.getResource() instanceof Parameters) { 1129 t.setResult(processValidationResult((Parameters) r.getResource(), vs != null ? vs.getUrl() : t.getVsObj() != null ? t.getVsObj().getUrl() : null, tc.getAddress())); 1130 if (txCache != null) { 1131 txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT); 1132 } 1133 } else { 1134 t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId())); 1135 } 1136 } 1137 } 1138 } 1139 1140 private Bundle processBatch(TerminologyClientContext tc, Bundle batch, Set<String> systems) { 1141 txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString()); 1142 if (terminologyClientManager == null) { 1143 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1144 } 1145 if (txLog != null) { 1146 txLog.clearLastId(); 1147 } 1148 Bundle resp = tc.getClient().validateBatch(batch); 1149 if (resp == null) { 1150 throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE)); 1151 } 1152 return resp; 1153 } 1154 1155 @Override 1156 public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl) { 1157 if (options == null) { 1158 options = ValidationOptions.defaults(); 1159 } 1160 // 1st pass: what is in the cache? 1161 // 2nd pass: What can we do internally 1162 // 3rd pass: hit the server 1163 for (CodingValidationRequest t : codes) { 1164 t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vsUrl, expParameters) : null); 1165 if (t.getCoding().hasSystem()) { 1166 codeSystemsUsed.add(t.getCoding().getSystem()); 1167 } 1168 if (txCache != null) { 1169 t.setResult(txCache.getValidation(t.getCacheToken())); 1170 } 1171 } 1172 ValueSet vs = fetchResource(ValueSet.class, vsUrl); 1173 if (options.isUseClient()) { 1174 if (vs != null) { 1175 for (CodingValidationRequest t : codes) { 1176 if (!t.hasResult()) { 1177 try { 1178 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs); 1179 vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient()); 1180 ValidationResult res = vsc.validateCode("Coding", t.getCoding()); 1181 if (txCache != null) { 1182 txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT); 1183 } 1184 t.setResult(res); 1185 } catch (Exception e) { 1186 } 1187 } 1188 } 1189 } 1190 } 1191 1192 for (CodingValidationRequest t : codes) { 1193 if (!t.hasResult()) { 1194 String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem(); 1195 if (!options.isUseServer()) { 1196 t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null)); 1197 } else if (unsupportedCodeSystems.contains(codeKey)) { 1198 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null)); 1199 } else if (noTerminologyServer) { 1200 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null)); 1201 } 1202 } 1203 } 1204 1205 if (expParameters == null) 1206 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1207 // for those that that failed, we try to validate on the server 1208 Bundle batch = new Bundle(); 1209 batch.setType(BundleType.BATCH); 1210 Set<String> systems = vs != null ? findRelevantSystems(vs) : new HashSet<>(); 1211 for (CodingValidationRequest codingValidationRequest : codes) { 1212 if (!codingValidationRequest.hasResult()) { 1213 Parameters pIn = constructParameters(options, codingValidationRequest, vsUrl); 1214 setTerminologyOptions(options, pIn); 1215 BundleEntryComponent be = batch.addEntry(); 1216 be.setResource(pIn); 1217 be.getRequest().setMethod(HTTPVerb.POST); 1218 if (vsUrl != null) { 1219 be.getRequest().setUrl("ValueSet/$validate-code"); 1220 } else { 1221 be.getRequest().setUrl("CodeSystem/$validate-code"); 1222 } 1223 be.setUserData("source", codingValidationRequest); 1224 systems.add(codingValidationRequest.getCoding().getSystem()); 1225 } 1226 } 1227 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false); 1228 1229 if (batch.getEntry().size() > 0) { 1230 Bundle resp = processBatch(tc, batch, systems); 1231 for (int i = 0; i < batch.getEntry().size(); i++) { 1232 CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); 1233 BundleEntryComponent r = resp.getEntry().get(i); 1234 1235 if (r.getResource() instanceof Parameters) { 1236 t.setResult(processValidationResult((Parameters) r.getResource(), vsUrl, tc.getAddress())); 1237 if (txCache != null) { 1238 txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT); 1239 } 1240 } else { 1241 t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId())); 1242 } 1243 } 1244 } 1245 } 1246 1247 private String getResponseText(Resource resource) { 1248 if (resource instanceof OperationOutcome) { 1249 return OperationOutcomeRenderer.toString((OperationOutcome) resource); 1250 } 1251 return "Todo"; 1252 } 1253 1254 @Override 1255 public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) { 1256 ValidationContextCarrier ctxt = new ValidationContextCarrier(); 1257 return validateCode(options, "Coding", code, vs, ctxt); 1258 } 1259 1260 public ValidationResult validateCode(ValidationOptions options, String path, Coding code, ValueSet vs) { 1261 ValidationContextCarrier ctxt = new ValidationContextCarrier(); 1262 return validateCode(options, path, code, vs, ctxt); 1263 } 1264 1265 private final String getCodeKey(Coding code) { 1266 return code.hasVersion() ? code.getSystem()+"|"+code.getVersion() : code.getSystem(); 1267 } 1268 1269 @Override 1270 public ValidationResult validateCode(final ValidationOptions optionsArg, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) { 1271 return validateCode(optionsArg, "Coding", code, vs, ctxt); 1272 } 1273 1274 public ValidationResult validateCode(final ValidationOptions optionsArg, String path, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) { 1275 1276 ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults(); 1277 1278 if (code.hasSystem()) { 1279 codeSystemsUsed.add(code.getSystem()); 1280 } 1281 1282 final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateValidationToken(options, code, vs, expParameters) : null; 1283 ValidationResult res = null; 1284 if (cachingAllowed && txCache != null) { 1285 res = txCache.getValidation(cacheToken); 1286 } 1287 if (res != null) { 1288 updateUnsupportedCodeSystems(res, code, getCodeKey(code)); 1289 return res; 1290 } 1291 1292 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1293 Set<String> unknownSystems = new HashSet<>(); 1294 1295 String localError = null; 1296 String localWarning = null; 1297 TerminologyServiceErrorClass type = TerminologyServiceErrorClass.UNKNOWN; 1298 if (options.isUseClient()) { 1299 // ok, first we try to validate locally 1300 try { 1301 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt); 1302 vsc.setUnknownSystems(unknownSystems); 1303 vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient()); 1304 if (!ValueSetUtilities.isServerSide(code.getSystem())) { 1305 res = vsc.validateCode(path, code.copy()); 1306 if (txCache != null && cachingAllowed) { 1307 txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); 1308 } 1309 return res; 1310 } 1311 } catch (VSCheckerException e) { 1312 if (e.isWarning()) { 1313 localWarning = e.getMessage(); 1314 } else { 1315 localError = e.getMessage(); 1316 } 1317 if (e.getIssues() != null) { 1318 issues.addAll(e.getIssues()); 1319 } 1320 type = e.getType(); 1321 } catch (TerminologyServiceProtectionException e) { 1322 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType()); 1323 iss.getDetails().setText(e.getMessage()); 1324 issues.add(iss); 1325 return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues); 1326 } catch (Exception e) { 1327// e.printStackTrace(); 1328 localError = e.getMessage(); 1329 } 1330 } 1331 1332 if (localError != null && !terminologyClientManager.hasClient()) { 1333 if (unknownSystems.size() > 0) { 1334 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems); 1335 } else { 1336 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues); 1337 } 1338 } 1339 if (localWarning != null && !terminologyClientManager.hasClient()) { 1340 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1341 } 1342 if (!options.isUseServer()) { 1343 if (localWarning != null) { 1344 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1345 } else { 1346 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localError), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1347 } 1348 } 1349 String codeKey = getCodeKey(code); 1350 if (unsupportedCodeSystems.contains(codeKey)) { 1351 return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, code.getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues); 1352 } 1353 1354 // if that failed, we try to validate on the server 1355 if (noTerminologyServer) { 1356 return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, code.getCode(), code.getSystem()), TerminologyServiceErrorClass.NOSERVICE, issues); 1357 } 1358 1359 Set<String> systems = findRelevantSystems(code, vs); 1360 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false); 1361 1362 String csumm = cachingAllowed && txCache != null ? txCache.summary(code) : null; 1363 if (cachingAllowed && txCache != null) { 1364 txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress()); 1365 } else { 1366 txLog("$validate "+csumm+" before cache exists on "+tc.getAddress()); 1367 } 1368 try { 1369 Parameters pIn = constructParameters(options, code); 1370 res = validateOnServer(tc, vs, pIn, options); 1371 } catch (Exception e) { 1372 res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR); 1373 } 1374 if (!res.isOk() && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && (localError != null && !localError.equals(ValueSetValidator.NO_TRY_THE_SERVER))) { 1375 res = new ValidationResult(IssueSeverity.ERROR, localError, null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(type); 1376 } 1377 if (!res.isOk() && localError != null) { 1378 res.setDiagnostics("Local Error: "+localError.trim()+". Server Error: "+res.getMessage()); 1379 } else if (!res.isOk() && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && res.getUnknownSystems() != null && res.getUnknownSystems().contains(codeKey) && localWarning != null) { 1380 // we had some problem evaluating locally, but the server doesn't know the code system, so we'll just go with the local error 1381 res = new ValidationResult(IssueSeverity.WARNING, localWarning, null); 1382 res.setDiagnostics("Local Warning: "+localWarning.trim()+". Server Error: "+res.getMessage()); 1383 return res; 1384 } 1385 updateUnsupportedCodeSystems(res, code, codeKey); 1386 if (cachingAllowed && txCache != null) { // we never cache unsupported code systems - we always keep trying (but only once per run) 1387 txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT); 1388 } 1389 return res; 1390 } 1391 1392 1393 /** 1394 * ask the terminology system whether parent subsumes child. 1395 * 1396 * @return true if it does, false if it doesn't, and null if it's not know whether it does 1397 */ 1398 public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) { 1399 ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults(); 1400 1401 if (parent.hasSystem()) { 1402 codeSystemsUsed.add(parent.getSystem()); 1403 } else { 1404 return null; 1405 } 1406 if (child.hasSystem()) { 1407 codeSystemsUsed.add(child.getSystem()); 1408 } else { 1409 return null; 1410 } 1411 1412 final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateSubsumesToken(options, parent, child, expParameters) : null; 1413 if (cachingAllowed && txCache != null) { 1414 Boolean res = txCache.getSubsumes(cacheToken); 1415 if (res != null) { 1416 return res; 1417 } 1418 } 1419 1420 if (options.isUseClient() && parent.getSystem().equals(child.getSystem())) { 1421 CodeSystem cs = fetchCodeSystem(parent.getSystem()); 1422 if (cs != null) { 1423 Boolean b = CodeSystemUtilities.subsumes(cs, parent.getCode(), child.getCode()); 1424 if (txCache != null && cachingAllowed) { 1425 txCache.cacheSubsumes(cacheToken, b, true); 1426 } 1427 return b; 1428 } 1429 } 1430 1431 if (!terminologyClientManager.hasClient() || !options.isUseServer() || unsupportedCodeSystems.contains(parent.getSystem()) || unsupportedCodeSystems.contains(child.getSystem()) || noTerminologyServer) { 1432 return null; 1433 } 1434 1435 Set<String> systems = new HashSet<>(); 1436 systems.add(parent.getSystem()); 1437 systems.add(child.getSystem()); 1438 TerminologyClientContext tc = terminologyClientManager.chooseServer(null, systems, false); 1439 1440 txLog("$subsumes "+parent.toString()+" > "+child.toString()+" on "+tc.getAddress()); 1441 1442 try { 1443 Parameters pIn = new Parameters(); 1444 pIn.addParameter().setName("codingA").setValue(parent); 1445 pIn.addParameter().setName("codingB").setValue(child); 1446 if (txLog != null) { 1447 txLog.clearLastId(); 1448 } 1449 Parameters pOut = tc.getClient().subsumes(pIn); 1450 return processSubsumesResult(pOut, tc.getClient().getAddress()); 1451 } catch (Exception e) { 1452 // e.printStackTrace(); 1453 } 1454 return null; 1455 } 1456 1457 1458 public Boolean processSubsumesResult(Parameters pOut, String server) { 1459 for (ParametersParameterComponent p : pOut.getParameter()) { 1460 if (p.hasValue()) { 1461 if (p.getName().equals("outcome")) { 1462 return Utilities.existsInList(p.getValue().primitiveValue(), "equivalent", "subsumes"); 1463 } 1464 } 1465 } 1466 return null; 1467 } 1468 1469 protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) { 1470 return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging()); 1471 } 1472 1473 protected ValueSetValidator constructValueSetCheckerSimple(ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) { 1474 return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, ctxt, expParameters, terminologyClientManager); 1475 } 1476 1477 protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) { 1478 return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, expParameters, terminologyClientManager); 1479 } 1480 1481 protected Parameters constructParameters(TerminologyClientContext tcd, ValueSet vs, boolean hierarchical) { 1482 Parameters p = expParameters.copy(); 1483 p.setParameter("includeDefinition", false); 1484 p.setParameter("excludeNested", !hierarchical); 1485 1486 addDependentResources(tcd, p, vs); 1487 p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); 1488 return p; 1489 } 1490 1491 protected Parameters constructParameters(ValidationOptions options, Coding coding) { 1492 Parameters pIn = new Parameters(); 1493 if (options.isGuessSystem()) { 1494 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1495 pIn.addParameter().setName("code").setValue(coding.getCodeElement()); 1496 } else { 1497 pIn.addParameter().setName("coding").setValue(coding); 1498 } 1499 setTerminologyOptions(options, pIn); 1500 return pIn; 1501 } 1502 1503 protected Parameters constructParameters(ValidationOptions options, CodeableConcept codeableConcept) { 1504 Parameters pIn = new Parameters(); 1505 pIn.addParameter().setName("codeableConcept").setValue(codeableConcept); 1506 setTerminologyOptions(options, pIn); 1507 return pIn; 1508 } 1509 1510 protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, ValueSet valueSet) { 1511 Parameters pIn = new Parameters(); 1512 if (options.isGuessSystem()) { 1513 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1514 pIn.addParameter().setName("code").setValue(codingValidationRequest.getCoding().getCodeElement()); 1515 } else { 1516 pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding()); 1517 } 1518 if (valueSet != null) { 1519 pIn.addParameter().setName("valueSet").setResource(valueSet); 1520 } 1521 1522 pIn.addParameters(expParameters); 1523 return pIn; 1524 } 1525 1526 protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, String vsUrl) { 1527 Parameters pIn = new Parameters(); 1528 if (options.isGuessSystem()) { 1529 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1530 pIn.addParameter().setName("code").setValue(codingValidationRequest.getCoding().getCodeElement()); 1531 } else { 1532 pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding()); 1533 } 1534 if (vsUrl != null) { 1535 pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl)); 1536 } 1537 pIn.addParameters(expParameters); 1538 return pIn; 1539 } 1540 1541 private void updateUnsupportedCodeSystems(ValidationResult res, Coding code, String codeKey) { 1542 if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && !code.hasVersion() && fetchCodeSystem(codeKey) == null) { 1543 unsupportedCodeSystems.add(codeKey); 1544 } 1545 } 1546 1547 private void setTerminologyOptions(ValidationOptions options, Parameters pIn) { 1548 if (options.hasLanguages()) { 1549 pIn.addParameter("displayLanguage", options.getLanguages().toString()); 1550 } 1551 if (options.isMembershipOnly()) { 1552 pIn.addParameter("valueset-membership-only", true); 1553 } 1554 if (options.isDisplayWarningMode()) { 1555 pIn.addParameter("lenient-display-validation", true); 1556 } 1557 if (options.isVersionFlexible()) { 1558 pIn.addParameter("default-to-latest-version", true); 1559 } 1560 } 1561 1562 @Override 1563 public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) { 1564 CacheToken cacheToken = txCache.generateValidationToken(options, code, vs, expParameters); 1565 ValidationResult res = null; 1566 if (cachingAllowed) { 1567 res = txCache.getValidation(cacheToken); 1568 if (res != null) { 1569 return res; 1570 } 1571 } 1572 for (Coding c : code.getCoding()) { 1573 if (c.hasSystem()) { 1574 codeSystemsUsed.add(c.getSystem()); 1575 } 1576 } 1577 Set<String> unknownSystems = new HashSet<>(); 1578 1579 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1580 1581 String localError = null; 1582 String localWarning = null; 1583 1584 if (options.isUseClient()) { 1585 // ok, first we try to validate locally 1586 try { 1587 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs); 1588 vsc.setUnknownSystems(unknownSystems); 1589 vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient()); 1590 res = vsc.validateCode("CodeableConcept", code); 1591 if (cachingAllowed) { 1592 txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); 1593 } 1594 return res; 1595 } catch (VSCheckerException e) { 1596 if (e.isWarning()) { 1597 localWarning = e.getMessage(); 1598 } else { 1599 localError = e.getMessage(); 1600 } 1601 if (e.getIssues() != null) { 1602 issues.addAll(e.getIssues()); 1603 } 1604 } catch (TerminologyServiceProtectionException e) { 1605 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType()); 1606 iss.getDetails().setText(e.getMessage()); 1607 issues.add(iss); 1608 return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues); 1609 } catch (Exception e) { 1610// e.printStackTrace(); 1611 localError = e.getMessage(); 1612 } 1613 } 1614 1615 if (localError != null && !terminologyClientManager.hasClient()) { 1616 if (unknownSystems.size() > 0) { 1617 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems); 1618 } else { 1619 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues); 1620 } 1621 } 1622 if (localWarning != null && !terminologyClientManager.hasClient()) { 1623 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1624 } 1625 1626 if (!options.isUseServer()) { 1627 return new ValidationResult(IssueSeverity.WARNING, "Unable to validate code without using server", TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null); 1628 } 1629 1630 // if that failed, we try to validate on the server 1631 if (noTerminologyServer) { 1632 return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services", TerminologyServiceErrorClass.NOSERVICE, null); 1633 } 1634 Set<String> systems = findRelevantSystems(code, vs); 1635 TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false); 1636 1637 txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs)+" on "+tc.getAddress()); 1638 try { 1639 Parameters pIn = constructParameters(options, code); 1640 res = validateOnServer(tc, vs, pIn, options); 1641 } catch (Exception e) { 1642 res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()); 1643 } 1644 if (cachingAllowed) { 1645 txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT); 1646 } 1647 return res; 1648 } 1649 1650 private Set<String> findRelevantSystems(ValueSet vs) { 1651 Set<String> set = new HashSet<>(); 1652 if (vs != null) { 1653 findRelevantSystems(set, vs); 1654 } 1655 return set; 1656 } 1657 1658 private Set<String> findRelevantSystems(CodeableConcept code, ValueSet vs) { 1659 Set<String> set = new HashSet<>(); 1660 if (vs != null) { 1661 findRelevantSystems(set, vs); 1662 } 1663 for (Coding c : code.getCoding()) { 1664 findRelevantSystems(set, c); 1665 } 1666 return set; 1667 } 1668 1669 private Set<String> findRelevantSystems(Coding code, ValueSet vs) { 1670 Set<String> set = new HashSet<>(); 1671 if (vs != null) { 1672 findRelevantSystems(set, vs); 1673 } 1674 if (code != null) { 1675 findRelevantSystems(set, code); 1676 } 1677 return set; 1678 } 1679 1680 private void findRelevantSystems(Set<String> set, ValueSet vs) { 1681 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1682 findRelevantSystems(set, inc); 1683 } 1684 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1685 findRelevantSystems(set, inc); 1686 } 1687 } 1688 1689 private void findRelevantSystems(Set<String> set, ConceptSetComponent inc) { 1690 if (inc.hasSystem()) { 1691 if (inc.hasVersion()) { 1692 set.add(inc.getSystem()+"|"+inc.getVersion()); 1693 } else { 1694 set.add(inc.getSystem()); 1695 } 1696 } 1697 for (CanonicalType u : inc.getValueSet()) { 1698 ValueSet vs = fetchResource(ValueSet.class, u.getValue()); 1699 if (vs != null) { 1700 findRelevantSystems(set, vs); 1701 } else { 1702 set.add(TerminologyClientManager.UNRESOLVED_VALUESET); 1703 } 1704 } 1705 } 1706 1707 private void findRelevantSystems(Set<String> set, Coding c) { 1708 if (c.hasSystem()) { 1709 if (c.hasVersion()) { 1710 set.add(c.getSystem()+"|"+c.getVersion()); 1711 } else { 1712 set.add(c.getSystem()); 1713 } 1714 } 1715 } 1716 1717 protected ValidationResult validateOnServer(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException { 1718 1719 if (vs != null) { 1720 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1721 codeSystemsUsed.add(inc.getSystem()); 1722 } 1723 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1724 codeSystemsUsed.add(inc.getSystem()); 1725 } 1726 } 1727 1728 addServerValidationParameters(tc, vs, pin, options); 1729 1730 if (txLog != null) { 1731 txLog.clearLastId(); 1732 } 1733 if (tc == null) { 1734 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1735 } 1736 Parameters pOut; 1737 if (vs == null) { 1738 pOut = tc.getClient().validateCS(pin); 1739 } else { 1740 pOut = tc.getClient().validateVS(pin); 1741 } 1742 return processValidationResult(pOut, vs == null ? null : vs.getUrl(), tc.getClient().getAddress()); 1743 } 1744 1745 protected void addServerValidationParameters(TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options) { 1746 boolean cache = false; 1747 if (vs != null) { 1748 if (terminologyClientContext != null && terminologyClientContext.isTxCaching() && terminologyClientContext.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) { 1749 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl())); 1750 if (vs.hasVersion()) { 1751 pin.addParameter().setName("valueSetVersion").setValue(new StringType(vs.getVersion())); 1752 } 1753 } else if (options.getVsAsUrl()){ 1754 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl())); 1755 } else { 1756 pin.addParameter().setName("valueSet").setResource(vs); 1757 if (vs.getUrl() != null) { 1758 terminologyClientContext.getCached().add(vs.getUrl()+"|"+ vs.getVersion()); 1759 } 1760 } 1761 cache = true; 1762 addDependentResources(terminologyClientContext, pin, vs); 1763 } 1764 pin.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); 1765 for (ParametersParameterComponent pp : pin.getParameter()) { 1766 if (pp.getName().equals("profile")) { 1767 throw new Error(formatMessage(I18nConstants.CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT)); 1768 } 1769 } 1770 if (expParameters == null) { 1771 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1772 } 1773 pin.addParameters(expParameters); 1774 1775 if (options.isDisplayWarningMode()) { 1776 pin.addParameter("mode","lenient-display-validation"); 1777 } 1778 } 1779 1780 private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ValueSet vs) { 1781 boolean cache = false; 1782 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1783 cache = addDependentResources(tc, pin, inc, vs) || cache; 1784 } 1785 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1786 cache = addDependentResources(tc, pin, inc, vs) || cache; 1787 } 1788 return cache; 1789 } 1790 1791 private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ConceptSetComponent inc, Resource src) { 1792 boolean cache = false; 1793 for (CanonicalType c : inc.getValueSet()) { 1794 ValueSet vs = fetchResource(ValueSet.class, c.getValue(), src); 1795 if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) { 1796 cache = checkAddToParams(tc, pin, vs) || cache; 1797 addDependentResources(tc, pin, vs); 1798 for (Extension ext : vs.getExtensionsByUrl(ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED)) { 1799 if (ext.hasValueCanonicalType()) { 1800 String url = ext.getValueCanonicalType().asStringValue(); 1801 CodeSystem supp = fetchResource(CodeSystem.class, url); 1802 if (supp != null) { 1803 cache = checkAddToParams(tc, pin, supp) || cache; 1804 } 1805 } 1806 } 1807 } 1808 } 1809 CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src); 1810 if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) { 1811 cache = checkAddToParams(tc, pin, cs) || cache; 1812 } 1813 for (CodeSystem supp : fetchResourcesByType(CodeSystem.class)) { 1814 if (supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem())) { 1815 cache = checkAddToParams(tc, pin, supp) || cache; 1816 } 1817 } 1818 return cache; 1819 } 1820 1821 private boolean checkAddToParams(TerminologyClientContext tc, Parameters pin, CanonicalResource cr) { 1822 boolean cache = false; 1823 boolean addToParams = false; 1824 if (tc.usingCache()) { 1825 if (!tc.alreadyCached(cr)) { 1826 tc.addToCache(cr); 1827 if (logger.isDebugLogging()) { 1828 logger.logMessage("add to cache: "+cr.getVUrl()); 1829 } 1830 addToParams = true; 1831 cache = true; 1832 } else { 1833 if (logger.isDebugLogging()) { 1834 logger.logMessage("already cached: "+cr.getVUrl()); 1835 } 1836 } 1837 } else { 1838 addToParams = true; 1839 } 1840 if (addToParams) { 1841 pin.addParameter().setName("tx-resource").setResource(cr); 1842 } 1843 return cache; 1844 } 1845 1846 private boolean hasCanonicalResource(Parameters pin, String name, String vUrl) { 1847 for (ParametersParameterComponent p : pin.getParameter()) { 1848 if (name.equals(p.getName()) && p.hasResource() && 1849 p.getResource() instanceof CanonicalResource && vUrl.equals(((CanonicalResource) p.getResource()).getVUrl())) { 1850 return true; 1851 } 1852 } 1853 return false; 1854 } 1855 1856 public ValidationResult processValidationResult(Parameters pOut, String vs, String server) { 1857 boolean ok = false; 1858 String message = "No Message returned"; 1859 String display = null; 1860 String system = null; 1861 String code = null; 1862 String version = null; 1863 boolean inactive = false; 1864 String status = null; 1865 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1866 Set<String> unknownSystems = new HashSet<>(); 1867 1868 TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN; 1869 for (ParametersParameterComponent p : pOut.getParameter()) { 1870 if (p.hasValue()) { 1871 if (p.getName().equals("result")) { 1872 ok = ((BooleanType) p.getValue()).getValue().booleanValue(); 1873 } else if (p.getName().equals("message")) { 1874 message = p.getValue().primitiveValue(); 1875 } else if (p.getName().equals("display")) { 1876 display = p.getValue().primitiveValue(); 1877 } else if (p.getName().equals("system")) { 1878 system = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1879 } else if (p.getName().equals("version")) { 1880 version = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1881 } else if (p.getName().equals("code")) { 1882 code = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1883 } else if (p.getName().equals("inactive")) { 1884 inactive = "true".equals(((PrimitiveType<?>) p.getValue()).asStringValue()); 1885 } else if (p.getName().equals("status")) { 1886 status = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1887 } else if (p.getName().equals("x-caused-by-unknown-system")) { 1888 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 1889 unknownSystems.add(((PrimitiveType<?>) p.getValue()).asStringValue()); 1890 } else if (p.getName().equals("x-unknown-system")) { 1891 unknownSystems.add(((PrimitiveType<?>) p.getValue()).asStringValue()); 1892 } else if (p.getName().equals("warning-withdrawn")) { 1893 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1894 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1895 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_WITHDRAWN : I18nConstants.MSG_WITHDRAWN_SRC, msg, vs, impliedType(msg))); 1896 issues.add(iss); 1897 } else if (p.getName().equals("warning-deprecated")) { 1898 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1899 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1900 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DEPRECATED : I18nConstants.MSG_DEPRECATED_SRC, msg, vs, impliedType(msg))); 1901 issues.add(iss); 1902 } else if (p.getName().equals("warning-retired")) { 1903 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1904 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1905 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_RETIRED : I18nConstants.MSG_RETIRED_SRC, msg, vs, impliedType(msg))); 1906 issues.add(iss); 1907 } else if (p.getName().equals("warning-experimental")) { 1908 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1909 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1910 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_EXPERIMENTAL : I18nConstants.MSG_EXPERIMENTAL_SRC, msg, vs, impliedType(msg))); 1911 issues.add(iss); 1912 } else if (p.getName().equals("warning-draft")) { 1913 String msg = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1914 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1915 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DRAFT : I18nConstants.MSG_DRAFT_SRC, msg, vs, impliedType(msg))); 1916 issues.add(iss); 1917 } else if (p.getName().equals("cause")) { 1918 try { 1919 IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue()); 1920 if (it == IssueType.UNKNOWN) { 1921 err = TerminologyServiceErrorClass.UNKNOWN; 1922 } else if (it == IssueType.NOTFOUND) { 1923 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 1924 } else if (it == IssueType.NOTSUPPORTED) { 1925 err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED; 1926 } else { 1927 err = null; 1928 } 1929 } catch (FHIRException e) { 1930 } 1931 } 1932 } else if (p.hasResource()) { 1933 if (p.getName().equals("issues")) { 1934 OperationOutcome oo = (OperationOutcome) p.getResource(); 1935 for (OperationOutcomeIssueComponent iss : oo.getIssue()) { 1936 iss.addExtension(ToolingExtensions.EXT_ISSUE_SERVER, new UrlType(server)); 1937 issues.add(iss); 1938 } 1939 } else { 1940 // nothing? 1941 } 1942 } 1943 } 1944 ValidationResult res = null; 1945 if (!ok) { 1946 res = new ValidationResult(IssueSeverity.ERROR, message, err, null).setTxLink(txLog.getLastId()); 1947 if (code != null) { 1948 res.setDefinition(new ConceptDefinitionComponent().setDisplay(display).setCode(code)); 1949 res.setDisplay(display); 1950 } 1951 } else if (message != null && !message.equals("No Message returned")) { 1952 res = new ValidationResult(IssueSeverity.WARNING, message, system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog.getLastId()); 1953 } else if (display != null) { 1954 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display).setTxLink(txLog.getLastId()); 1955 } else { 1956 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setCode(code), null).setTxLink(txLog.getLastId()); 1957 } 1958 res.setIssues(issues); 1959 res.setStatus(inactive, status); 1960 res.setUnknownSystems(unknownSystems); 1961 res.setServer(server); 1962 return res; 1963 } 1964 1965 // -------------------------------------------------------------------------------------------------------------------------------------------------------- 1966 1967 private Object impliedType(String msg) { 1968 if (msg.contains("/CodeSystem")) { 1969 return "CodeSystem"; 1970 } 1971 if (msg.contains("/ValueSet")) { 1972 return "ValueSet"; 1973 } 1974 return "item"; 1975 } 1976 1977 public void initTxCache(String cachePath) throws FileNotFoundException, FHIRException, IOException { 1978 if (cachePath != null) { 1979 txCache = new TerminologyCache(lock, cachePath); 1980 initTxCache(txCache); 1981 } 1982 } 1983 1984 public void initTxCache(TerminologyCache cache) { 1985 txCache = cache; 1986 terminologyClientManager.setCache(txCache); 1987 } 1988 1989 public void clearTSCache(String url) throws Exception { 1990 txCache.removeCS(url); 1991 } 1992 1993 public boolean isCanRunWithoutTerminology() { 1994 return canRunWithoutTerminology; 1995 } 1996 1997 public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) { 1998 this.canRunWithoutTerminology = canRunWithoutTerminology; 1999 } 2000 2001 public void setLogger(@Nonnull org.hl7.fhir.r5.context.ILoggingService logger) { 2002 this.logger = logger; 2003 } 2004 2005 public Parameters getExpansionParameters() { 2006 return expParameters; 2007 } 2008 2009 public void setExpansionParameters(Parameters expParameters) { 2010 this.expParameters = expParameters; 2011 this.terminologyClientManager.setExpansionParameters(expParameters); 2012 } 2013 2014 @Override 2015 public boolean isNoTerminologyServer() { 2016 return noTerminologyServer || !terminologyClientManager.hasClient(); 2017 } 2018 2019 public void setNoTerminologyServer(boolean noTerminologyServer) { 2020 this.noTerminologyServer = noTerminologyServer; 2021 } 2022 2023 public String getName() { 2024 return name; 2025 } 2026 2027 public void setName(String name) { 2028 this.name = name; 2029 } 2030 2031 2032 public List<String> getResourceNames(FhirPublication fhirVersion) { 2033 return getResourceNames(); 2034 } 2035 2036 public Set<String> getResourceNamesAsSet(FhirPublication fhirVersion) { 2037 return getResourceNamesAsSet(); 2038 } 2039 2040 @Override 2041 public Set<String> getResourceNamesAsSet() { 2042 Set<String> res = new HashSet<String>(); 2043 res.addAll(getResourceNames()); 2044 return res; 2045 } 2046 2047 public boolean isAllowLoadingDuplicates() { 2048 return allowLoadingDuplicates; 2049 } 2050 2051 public void setAllowLoadingDuplicates(boolean allowLoadingDuplicates) { 2052 this.allowLoadingDuplicates = allowLoadingDuplicates; 2053 } 2054 2055 @Override 2056 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException { 2057 return fetchResourceWithException(class_, uri, null); 2058 } 2059 2060 public <T extends Resource> T fetchResourceWithException(String cls, String uri) throws FHIRException { 2061 return fetchResourceWithExceptionByVersion(cls, uri, null, null); 2062 } 2063 2064 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, Resource sourceForReference) throws FHIRException { 2065 return fetchResourceWithExceptionByVersion(class_, uri, null, sourceForReference); 2066 } 2067 2068 @SuppressWarnings("unchecked") 2069 public <T extends Resource> T fetchResourceWithExceptionByVersion(Class<T> class_, String uri, String version, Resource sourceForReference) throws FHIRException { 2070 if (uri == null) { 2071 return null; 2072 } 2073 if (uri.startsWith("#")) { 2074 if (sourceForReference != null && sourceForReference instanceof DomainResource) { 2075 for (Resource r : ((DomainResource) sourceForReference).getContained()) { 2076 if (r.getClass() == class_ &&( "#"+r.getIdBase()).equals(uri)) { 2077 if (r instanceof CanonicalResource) { 2078 CanonicalResource cr = (CanonicalResource) r; 2079 if (!cr.hasUrl()) { 2080 cr.setUrl(Utilities.makeUuidUrn()); 2081 } 2082 } 2083 return (T) r; 2084 } 2085 } 2086 } 2087 return null; 2088 } 2089 2090 if (QA_CHECK_REFERENCE_SOURCE) { 2091 // it can be tricky to trace the source of a reference correctly. The code isn't water tight, 2092 // particularly around snapshot generation. Enable this code to check that the references are 2093 // correct (but it's slow) 2094 if (sourceForReference != null && uri.contains("ValueSet")) { 2095 if (!ResourceUtilities.hasURL(uri, sourceForReference)) { 2096 System.out.print("Claimed source doesn't have url in it: "+sourceForReference.fhirType()+"/"+sourceForReference.getIdPart()+" -> "+uri); 2097 System.out.println(); 2098 } 2099 } 2100 } 2101 2102 List<String> pvlist = new ArrayList<>(); 2103 if (sourceForReference != null && sourceForReference.getSourcePackage() != null) { 2104 populatePVList(pvlist, sourceForReference.getSourcePackage()); 2105 } 2106 2107 if (class_ == StructureDefinition.class) { 2108 uri = ProfileUtilities.sdNs(uri, null); 2109 } 2110 synchronized (lock) { 2111 2112 if (version == null) { 2113 if (uri.contains("|")) { 2114 version = uri.substring(uri.lastIndexOf("|")+1); 2115 uri = uri.substring(0, uri.lastIndexOf("|")); 2116 } 2117 } else { 2118 assert !uri.contains("|"); 2119 } 2120 if (uri.contains("#")) { 2121 uri = uri.substring(0, uri.indexOf("#")); 2122 } 2123 if (class_ == Resource.class || class_ == null) { 2124 if (structures.has(uri)) { 2125 return (T) structures.get(uri, version, pvlist); 2126 } 2127 if (guides.has(uri)) { 2128 return (T) guides.get(uri, version, pvlist); 2129 } 2130 if (capstmts.has(uri)) { 2131 return (T) capstmts.get(uri, version, pvlist); 2132 } 2133 if (measures.has(uri)) { 2134 return (T) measures.get(uri, version, pvlist); 2135 } 2136 if (libraries.has(uri)) { 2137 return (T) libraries.get(uri, version, pvlist); 2138 } 2139 if (valueSets.has(uri)) { 2140 return (T) valueSets.get(uri, version, pvlist); 2141 } 2142 if (codeSystems.has(uri)) { 2143 return (T) codeSystems.get(uri, version, pvlist); 2144 } 2145 if (systems.has(uri)) { 2146 return (T) systems.get(uri, version, pvlist); 2147 } 2148 if (operations.has(uri)) { 2149 return (T) operations.get(uri, version, pvlist); 2150 } 2151 if (searchParameters.has(uri)) { 2152 return (T) searchParameters.get(uri, version, pvlist); 2153 } 2154 if (plans.has(uri)) { 2155 return (T) plans.get(uri, version, pvlist); 2156 } 2157 if (maps.has(uri)) { 2158 return (T) maps.get(uri, version, pvlist); 2159 } 2160 if (transforms.has(uri)) { 2161 return (T) transforms.get(uri, version, pvlist); 2162 } 2163 if (actors.has(uri)) { 2164 return (T) transforms.get(uri, version, pvlist); 2165 } 2166 if (requirements.has(uri)) { 2167 return (T) transforms.get(uri, version, pvlist); 2168 } 2169 if (questionnaires.has(uri)) { 2170 return (T) questionnaires.get(uri, version, pvlist); 2171 } 2172 2173 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 2174 for (ResourceProxy r : rt.values()) { 2175 if (uri.equals(r.getUrl())) { 2176 if (version == null || version == r.getResource().getMeta().getVersionId()) { 2177 return (T) r.getResource(); 2178 } 2179 } 2180 } 2181 } 2182 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 2183 return null; 2184 } 2185 2186 // it might be a special URL. 2187// if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 2188// Resource res = null; // findTxValueSet(uri); 2189// if (res != null) { 2190// return (T) res; 2191// } 2192// } 2193 return null; 2194 } else if (class_ == ImplementationGuide.class) { 2195 return (T) guides.get(uri, version, pvlist); 2196 } else if (class_ == CapabilityStatement.class) { 2197 return (T) capstmts.get(uri, version, pvlist); 2198 } else if (class_ == Measure.class) { 2199 return (T) measures.get(uri, version, pvlist); 2200 } else if (class_ == Library.class) { 2201 return (T) libraries.get(uri, version, pvlist); 2202 } else if (class_ == StructureDefinition.class) { 2203 return (T) structures.get(uri, version, pvlist); 2204 } else if (class_ == StructureMap.class) { 2205 return (T) transforms.get(uri, version, pvlist); 2206 } else if (class_ == NamingSystem.class) { 2207 return (T) systems.get(uri, version, pvlist); 2208 } else if (class_ == ValueSet.class) { 2209 return (T) valueSets.get(uri, version, pvlist); 2210 } else if (class_ == CodeSystem.class) { 2211 return (T) codeSystems.get(uri, version, pvlist); 2212 } else if (class_ == ConceptMap.class) { 2213 return (T) maps.get(uri, version, pvlist); 2214 } else if (class_ == ActorDefinition.class) { 2215 return (T) actors.get(uri, version, pvlist); 2216 } else if (class_ == Requirements.class) { 2217 return (T) requirements.get(uri, version, pvlist); 2218 } else if (class_ == PlanDefinition.class) { 2219 return (T) plans.get(uri, version, pvlist); 2220 } else if (class_ == OperationDefinition.class) { 2221 OperationDefinition od = operations.get(uri, version); 2222 return (T) od; 2223 } else if (class_ == Questionnaire.class) { 2224 return (T) questionnaires.get(uri, version, pvlist); 2225 } else if (class_ == SearchParameter.class) { 2226 SearchParameter res = searchParameters.get(uri, version, pvlist); 2227 return (T) res; 2228 } 2229 if (class_ == CodeSystem.class && codeSystems.has(uri)) { 2230 return (T) codeSystems.get(uri, version, pvlist); 2231 } 2232 if (class_ == ValueSet.class && valueSets.has(uri)) { 2233 return (T) valueSets.get(uri, version, pvlist); 2234 } 2235 2236 if (class_ == Questionnaire.class) { 2237 return (T) questionnaires.get(uri, version, pvlist); 2238 } 2239 if (supportedCodeSystems.contains(uri)) { 2240 return null; 2241 } 2242 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 2243 } 2244 } 2245 2246 private void populatePVList(List<String> pvlist, PackageInformation sourcePackage) { 2247 pvlist.add(sourcePackage.getVID()); 2248 List<String> toadd = new ArrayList<>(); 2249 do { 2250 toadd.clear(); 2251 for (String s : pvlist) { 2252 PackageInformation pi = packages.get(s); 2253 if (pi != null) { 2254 for (String v : pi.getDependencies()) { 2255 if (!pvlist.contains(v) && !toadd.contains(v)) { 2256 toadd.add(v); 2257 } 2258 } 2259 } 2260 } 2261 pvlist.addAll(toadd); 2262 } while (toadd.size() > 0); 2263 } 2264 2265 public PackageInformation getPackageForUrl(String uri) { 2266 if (uri == null) { 2267 return null; 2268 } 2269 uri = ProfileUtilities.sdNs(uri, null); 2270 2271 synchronized (lock) { 2272 2273 String version = null; 2274 if (uri.contains("|")) { 2275 version = uri.substring(uri.lastIndexOf("|")+1); 2276 uri = uri.substring(0, uri.lastIndexOf("|")); 2277 } 2278 if (uri.contains("#")) { 2279 uri = uri.substring(0, uri.indexOf("#")); 2280 } 2281 if (structures.has(uri)) { 2282 return structures.getPackageInfo(uri, version); 2283 } 2284 if (guides.has(uri)) { 2285 return guides.getPackageInfo(uri, version); 2286 } 2287 if (capstmts.has(uri)) { 2288 return capstmts.getPackageInfo(uri, version); 2289 } 2290 if (measures.has(uri)) { 2291 return measures.getPackageInfo(uri, version); 2292 } 2293 if (libraries.has(uri)) { 2294 return libraries.getPackageInfo(uri, version); 2295 } 2296 if (valueSets.has(uri)) { 2297 return valueSets.getPackageInfo(uri, version); 2298 } 2299 if (codeSystems.has(uri)) { 2300 return codeSystems.getPackageInfo(uri, version); 2301 } 2302 if (operations.has(uri)) { 2303 return operations.getPackageInfo(uri, version); 2304 } 2305 if (searchParameters.has(uri)) { 2306 return searchParameters.getPackageInfo(uri, version); 2307 } 2308 if (plans.has(uri)) { 2309 return plans.getPackageInfo(uri, version); 2310 } 2311 if (maps.has(uri)) { 2312 return maps.getPackageInfo(uri, version); 2313 } 2314 if (transforms.has(uri)) { 2315 return transforms.getPackageInfo(uri, version); 2316 } 2317 if (actors.has(uri)) { 2318 return actors.getPackageInfo(uri, version); 2319 } 2320 if (requirements.has(uri)) { 2321 return requirements.getPackageInfo(uri, version); 2322 } 2323 if (questionnaires.has(uri)) { 2324 return questionnaires.getPackageInfo(uri, version); 2325 } 2326 return null; 2327 } 2328 } 2329 2330 @SuppressWarnings("unchecked") 2331 public <T extends Resource> T fetchResourceWithExceptionByVersion(String cls, String uri, String version, CanonicalResource source) throws FHIRException { 2332 if (uri == null) { 2333 return null; 2334 } 2335 2336 if ("StructureDefinition".equals(cls)) { 2337 uri = ProfileUtilities.sdNs(uri, null); 2338 } 2339 synchronized (lock) { 2340 2341 if (version == null) { 2342 if (uri.contains("|")) { 2343 version = uri.substring(uri.lastIndexOf("|")+1); 2344 uri = uri.substring(0, uri.lastIndexOf("|")); 2345 } 2346 } else { 2347 boolean b = !uri.contains("|"); 2348 assert b; 2349 } 2350 if (uri.contains("#")) { 2351 uri = uri.substring(0, uri.indexOf("#")); 2352 } 2353 if (cls == null || "Resource".equals(cls)) { 2354 if (structures.has(uri)) { 2355 return (T) structures.get(uri, version); 2356 } 2357 if (guides.has(uri)) { 2358 return (T) guides.get(uri, version); 2359 } 2360 if (capstmts.has(uri)) { 2361 return (T) capstmts.get(uri, version); 2362 } 2363 if (measures.has(uri)) { 2364 return (T) measures.get(uri, version); 2365 } 2366 if (libraries.has(uri)) { 2367 return (T) libraries.get(uri, version); 2368 } 2369 if (valueSets.has(uri)) { 2370 return (T) valueSets.get(uri, version); 2371 } 2372 if (codeSystems.has(uri)) { 2373 return (T) codeSystems.get(uri, version); 2374 } 2375 if (operations.has(uri)) { 2376 return (T) operations.get(uri, version); 2377 } 2378 if (searchParameters.has(uri)) { 2379 return (T) searchParameters.get(uri, version); 2380 } 2381 if (plans.has(uri)) { 2382 return (T) plans.get(uri, version); 2383 } 2384 if (maps.has(uri)) { 2385 return (T) maps.get(uri, version); 2386 } 2387 if (transforms.has(uri)) { 2388 return (T) transforms.get(uri, version); 2389 } 2390 if (actors.has(uri)) { 2391 return (T) actors.get(uri, version); 2392 } 2393 if (requirements.has(uri)) { 2394 return (T) requirements.get(uri, version); 2395 } 2396 if (questionnaires.has(uri)) { 2397 return (T) questionnaires.get(uri, version); 2398 } 2399 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 2400 for (ResourceProxy r : rt.values()) { 2401 if (uri.equals(r.getUrl())) { 2402 return (T) r.getResource(); 2403 } 2404 } 2405 } 2406 } else if ("ImplementationGuide".equals(cls)) { 2407 return (T) guides.get(uri, version); 2408 } else if ("CapabilityStatement".equals(cls)) { 2409 return (T) capstmts.get(uri, version); 2410 } else if ("Measure".equals(cls)) { 2411 return (T) measures.get(uri, version); 2412 } else if ("Library".equals(cls)) { 2413 return (T) libraries.get(uri, version); 2414 } else if ("StructureDefinition".equals(cls)) { 2415 return (T) structures.get(uri, version); 2416 } else if ("StructureMap".equals(cls)) { 2417 return (T) transforms.get(uri, version); 2418 } else if ("Requirements".equals(cls)) { 2419 return (T) requirements.get(uri, version); 2420 } else if ("ActorDefinition".equals(cls)) { 2421 return (T) actors.get(uri, version); 2422 } else if ("ValueSet".equals(cls)) { 2423 return (T) valueSets.get(uri, version); 2424 } else if ("CodeSystem".equals(cls)) { 2425 return (T) codeSystems.get(uri, version); 2426 } else if ("ConceptMap".equals(cls)) { 2427 return (T) maps.get(uri, version); 2428 } else if ("PlanDefinition".equals(cls)) { 2429 return (T) plans.get(uri, version); 2430 } else if ("OperationDefinition".equals(cls)) { 2431 OperationDefinition od = operations.get(uri, version); 2432 return (T) od; 2433 } else if ("Questionnaire.class".equals(cls)) { 2434 return (T) questionnaires.get(uri, version); 2435 } else if ("SearchParameter.class".equals(cls)) { 2436 SearchParameter res = searchParameters.get(uri, version); 2437 return (T) res; 2438 } 2439 if ("CodeSystem".equals(cls) && codeSystems.has(uri)) { 2440 return (T) codeSystems.get(uri, version); 2441 } 2442 if ("ValueSet".equals(cls) && valueSets.has(uri)) { 2443 return (T) valueSets.get(uri, version); 2444 } 2445 2446 if ("Questionnaire".equals(cls)) { 2447 return (T) questionnaires.get(uri, version); 2448 } 2449 if (cls == null) { 2450 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 2451 return null; 2452 } 2453 2454 // it might be a special URL. 2455 if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 2456 Resource res = null; // findTxValueSet(uri); 2457 if (res != null) { 2458 return (T) res; 2459 } 2460 } 2461 return null; 2462 } 2463 if (supportedCodeSystems.contains(uri)) { 2464 return null; 2465 } 2466 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 2467 } 2468 } 2469 2470 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_, FhirPublication fhirVersion) { 2471 return fetchResourcesByType(class_); 2472 } 2473 2474 @SuppressWarnings("unchecked") 2475 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_) { 2476 2477 List<T> res = new ArrayList<>(); 2478 2479 synchronized (lock) { 2480 2481 if (class_ == Resource.class || class_ == DomainResource.class || class_ == CanonicalResource.class || class_ == null) { 2482 res.addAll((List<T>) structures.getList()); 2483 res.addAll((List<T>) guides.getList()); 2484 res.addAll((List<T>) capstmts.getList()); 2485 res.addAll((List<T>) measures.getList()); 2486 res.addAll((List<T>) libraries.getList()); 2487 res.addAll((List<T>) valueSets.getList()); 2488 res.addAll((List<T>) codeSystems.getList()); 2489 res.addAll((List<T>) operations.getList()); 2490 res.addAll((List<T>) searchParameters.getList()); 2491 res.addAll((List<T>) plans.getList()); 2492 res.addAll((List<T>) maps.getList()); 2493 res.addAll((List<T>) transforms.getList()); 2494 res.addAll((List<T>) questionnaires.getList()); 2495 res.addAll((List<T>) systems.getList()); 2496 res.addAll((List<T>) actors.getList()); 2497 res.addAll((List<T>) requirements.getList()); 2498 } else if (class_ == ImplementationGuide.class) { 2499 res.addAll((List<T>) guides.getList()); 2500 } else if (class_ == CapabilityStatement.class) { 2501 res.addAll((List<T>) capstmts.getList()); 2502 } else if (class_ == Measure.class) { 2503 res.addAll((List<T>) measures.getList()); 2504 } else if (class_ == Library.class) { 2505 res.addAll((List<T>) libraries.getList()); 2506 } else if (class_ == StructureDefinition.class) { 2507 res.addAll((List<T>) structures.getList()); 2508 } else if (class_ == StructureMap.class) { 2509 res.addAll((List<T>) transforms.getList()); 2510 } else if (class_ == ValueSet.class) { 2511 res.addAll((List<T>) valueSets.getList()); 2512 } else if (class_ == CodeSystem.class) { 2513 res.addAll((List<T>) codeSystems.getList()); 2514 } else if (class_ == NamingSystem.class) { 2515 res.addAll((List<T>) systems.getList()); 2516 } else if (class_ == ActorDefinition.class) { 2517 res.addAll((List<T>) actors.getList()); 2518 } else if (class_ == Requirements.class) { 2519 res.addAll((List<T>) requirements.getList()); 2520 } else if (class_ == ConceptMap.class) { 2521 res.addAll((List<T>) maps.getList()); 2522 } else if (class_ == PlanDefinition.class) { 2523 res.addAll((List<T>) plans.getList()); 2524 } else if (class_ == OperationDefinition.class) { 2525 res.addAll((List<T>) operations.getList()); 2526 } else if (class_ == Questionnaire.class) { 2527 res.addAll((List<T>) questionnaires.getList()); 2528 } else if (class_ == SearchParameter.class) { 2529 res.addAll((List<T>) searchParameters.getList()); 2530 } 2531 } 2532 return res; 2533 } 2534 2535 private Set<String> notCanonical = new HashSet<String>(); 2536 2537 protected IWorkerContextManager.IPackageLoadingTracker packageTracker; 2538 private boolean forPublication; 2539 private boolean cachingAllowed = true; 2540 private static boolean nsFailHasFailed; 2541 2542 public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) { 2543 return fetchResourceById(type, uri); 2544 } 2545 2546 @Override 2547 public Resource fetchResourceById(String type, String uri) { 2548 synchronized (lock) { 2549 String[] parts = uri.split("\\/"); 2550 if (!Utilities.noString(type) && parts.length == 1) { 2551 if (allResourcesById.containsKey(type)) { 2552 ResourceProxy res = allResourcesById.get(type).get(parts[0]); 2553 return res == null ? null : res.getResource(); 2554 } else { 2555 return null; 2556 } 2557 } 2558 if (parts.length >= 2) { 2559 if (!Utilities.noString(type)) { 2560 if (!type.equals(parts[parts.length-2])) { 2561 throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri)); 2562 } 2563 } 2564 return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource(); 2565 } else { 2566 throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri)); 2567 } 2568 } 2569 } 2570 2571 public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource sourceForReference) { 2572 try { 2573 return fetchResourceWithException(class_, uri, sourceForReference); 2574 } catch (FHIRException e) { 2575 throw new Error(e); 2576 } 2577 } 2578 2579 public <T extends Resource> T fetchResource(Class<T> class_, String uri, FhirPublication fhirVersion) { 2580 return fetchResource(class_, uri); 2581 } 2582 2583 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 2584 try { 2585 return fetchResourceWithException(class_, uri, null); 2586 } catch (FHIRException e) { 2587 throw new Error(e); 2588 } 2589 } 2590 2591 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version, FhirPublication fhirVersion) { 2592 return fetchResource(class_, uri, version); 2593 } 2594 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) { 2595 try { 2596 return fetchResourceWithExceptionByVersion(class_, uri, version, null); 2597 } catch (FHIRException e) { 2598 throw new Error(e); 2599 } 2600 } 2601 2602 @Override 2603 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 2604 try { 2605 return fetchResourceWithException(class_, uri) != null; 2606 } catch (Exception e) { 2607 return false; 2608 } 2609 } 2610 2611 public <T extends Resource> boolean hasResource(String cls, String uri) { 2612 try { 2613 return fetchResourceWithException(cls, uri) != null; 2614 } catch (Exception e) { 2615 return false; 2616 } 2617 } 2618 2619 public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version) { 2620 try { 2621 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2622 } catch (Exception e) { 2623 return false; 2624 } 2625 } 2626 2627 public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version) { 2628 try { 2629 return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null; 2630 } catch (Exception e) { 2631 return false; 2632 } 2633 } 2634 2635 @Override 2636 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, FhirPublication fhirVersion) { 2637 try { 2638 return fetchResourceWithException(class_, uri) != null; 2639 } catch (Exception e) { 2640 return false; 2641 } 2642 } 2643 2644 public <T extends Resource> boolean hasResource(String cls, String uri, FhirPublication fhirVersion) { 2645 try { 2646 return fetchResourceWithException(cls, uri) != null; 2647 } catch (Exception e) { 2648 return false; 2649 } 2650 } 2651 2652 public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version, FhirPublication fhirVersion) { 2653 try { 2654 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2655 } catch (Exception e) { 2656 return false; 2657 } 2658 } 2659 2660 public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version, FhirPublication fhirVersion) { 2661 try { 2662 return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null; 2663 } catch (Exception e) { 2664 return false; 2665 } 2666 } 2667 2668 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, Resource sourceOfReference) { 2669 try { 2670 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2671 } catch (Exception e) { 2672 return false; 2673 } 2674 } 2675 2676 public void reportStatus(JsonObject json) { 2677 synchronized (lock) { 2678 json.addProperty("codeystem-count", codeSystems.size()); 2679 json.addProperty("valueset-count", valueSets.size()); 2680 json.addProperty("conceptmap-count", maps.size()); 2681 json.addProperty("transforms-count", transforms.size()); 2682 json.addProperty("structures-count", structures.size()); 2683 json.addProperty("guides-count", guides.size()); 2684 json.addProperty("statements-count", capstmts.size()); 2685 json.addProperty("measures-count", measures.size()); 2686 json.addProperty("libraries-count", libraries.size()); 2687 } 2688 } 2689 2690 2691 public void dropResource(Resource r) throws FHIRException { 2692 dropResource(r.fhirType(), r.getId()); 2693 } 2694 2695 public void dropResource(String fhirType, String id) { 2696 synchronized (lock) { 2697 2698 Map<String, ResourceProxy> map = allResourcesById.get(fhirType); 2699 if (map == null) { 2700 map = new HashMap<String, ResourceProxy>(); 2701 allResourcesById.put(fhirType, map); 2702 } 2703 if (map.containsKey(id)) { 2704 map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions) 2705 } 2706 2707 if (fhirType.equals("StructureDefinition")) { 2708 structures.drop(id); 2709 typeManager.reload(); 2710 } else if (fhirType.equals("ImplementationGuide")) { 2711 guides.drop(id); 2712 } else if (fhirType.equals("CapabilityStatement")) { 2713 capstmts.drop(id); 2714 } else if (fhirType.equals("Measure")) { 2715 measures.drop(id); 2716 } else if (fhirType.equals("Library")) { 2717 libraries.drop(id); 2718 } else if (fhirType.equals("ValueSet")) { 2719 valueSets.drop(id); 2720 } else if (fhirType.equals("CodeSystem")) { 2721 codeSystems.drop(id); 2722 } else if (fhirType.equals("OperationDefinition")) { 2723 operations.drop(id); 2724 } else if (fhirType.equals("Questionnaire")) { 2725 questionnaires.drop(id); 2726 } else if (fhirType.equals("ConceptMap")) { 2727 maps.drop(id); 2728 } else if (fhirType.equals("StructureMap")) { 2729 transforms.drop(id); 2730 } else if (fhirType.equals("NamingSystem")) { 2731 systems.drop(id); 2732 systemUrlMap = null; 2733 } else if (fhirType.equals("ActorDefinition")) { 2734 actors.drop(id); 2735 } else if (fhirType.equals("Requirements")) { 2736 requirements.drop(id); 2737 } 2738 } 2739 } 2740 2741 private <T extends CanonicalResource> void dropMetadataResource(Map<String, T> map, String id) { 2742 T res = map.get(id); 2743 if (res != null) { 2744 map.remove(id); 2745 if (map.containsKey(res.getUrl())) { 2746 map.remove(res.getUrl()); 2747 } 2748 if (res.getVersion() != null) { 2749 if (map.containsKey(res.getUrl()+"|"+res.getVersion())) { 2750 map.remove(res.getUrl()+"|"+res.getVersion()); 2751 } 2752 } 2753 } 2754 } 2755 2756 2757 public String listSupportedSystems() { 2758 synchronized (lock) { 2759 String sl = null; 2760 for (String s : supportedCodeSystems) { 2761 sl = sl == null ? s : sl + "\r\n" + s; 2762 } 2763 return sl; 2764 } 2765 } 2766 2767 2768 public int totalCount() { 2769 synchronized (lock) { 2770 return valueSets.size() + maps.size() + structures.size() + transforms.size(); 2771 } 2772 } 2773 2774 public List<ConceptMap> listMaps() { 2775 List<ConceptMap> m = new ArrayList<ConceptMap>(); 2776 synchronized (lock) { 2777 maps.listAll(m); 2778 } 2779 return m; 2780 } 2781 2782 public List<StructureDefinition> listStructures() { 2783 List<StructureDefinition> m = new ArrayList<StructureDefinition>(); 2784 synchronized (lock) { 2785 structures.listAll(m); 2786 } 2787 return m; 2788 } 2789 2790 public StructureDefinition getStructure(String code) { 2791 synchronized (lock) { 2792 return structures.get(code); 2793 } 2794 } 2795 2796 private String getUri(NamingSystem ns) { 2797 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 2798 if (id.getType() == NamingSystemIdentifierType.URI) { 2799 return id.getValue(); 2800 } 2801 } 2802 return null; 2803 } 2804 2805 private boolean hasOid(NamingSystem ns, String oid) { 2806 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 2807 if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid)) { 2808 return true; 2809 } 2810 } 2811 return false; 2812 } 2813 2814 public void cacheVS(JsonObject json, Map<String, ValidationResult> t) { 2815 synchronized (lock) { 2816 validationCache.put(json.get("url").getAsString(), t); 2817 } 2818 } 2819 2820 public SearchParameter getSearchParameter(String code) { 2821 synchronized (lock) { 2822 return searchParameters.get(code); 2823 } 2824 } 2825 2826 @Override 2827 public org.hl7.fhir.r5.context.ILoggingService getLogger() { 2828 return logger; 2829 } 2830 2831 2832 public StructureDefinition fetchTypeDefinition(String typeName, FhirPublication fhirVersion) { 2833 return fetchTypeDefinition(typeName); 2834 } 2835 2836 @Override 2837 public StructureDefinition fetchTypeDefinition(String typeName) { 2838 if (Utilities.isAbsoluteUrl(typeName)) { 2839 StructureDefinition res = fetchResource(StructureDefinition.class, typeName); 2840 if (res != null) { 2841 return res; 2842 } 2843 } 2844 StructureDefinition p = typeManager.fetchTypeDefinition(typeName); 2845 if (p != null && !p.isGeneratedSnapshot()) { 2846 if (p.isGeneratingSnapshot()) { 2847 throw new FHIRException("Attempt to fetch the profile "+p.getVersionedUrl()+" while generating the snapshot for it"); 2848 } 2849 try { 2850 if (logger.isDebugLogging()) { 2851 System.out.println("Generating snapshot for "+p.getVersionedUrl()); 2852 } 2853 p.setGeneratingSnapshot(true); 2854 try { 2855 new ContextUtilities(this).generateSnapshot(p); 2856 } finally { 2857 p.setGeneratingSnapshot(false); 2858 } 2859 } catch (Exception e) { 2860 // not sure what to do in this case? 2861 System.out.println("Unable to generate snapshot @5 for "+p.getVersionedUrl()+": "+e.getMessage()); 2862 if (logger.isDebugLogging()) { 2863 e.printStackTrace(); 2864 } 2865 } 2866 } 2867 return p; 2868 } 2869 2870 @Override 2871 public List<StructureDefinition> fetchTypeDefinitions(String typeName) { 2872 return typeManager.getDefinitions(typeName); 2873 } 2874 2875 @Override 2876 public List<StructureDefinition> fetchTypeDefinitions(String typeName, FhirPublication fhirVersion) { 2877 return typeManager.getDefinitions(typeName); 2878 } 2879 2880 2881 public boolean isPrimitiveType(String type) { 2882 return typeManager.isPrimitive(type); 2883 } 2884 2885 public boolean isDataType(String type) { 2886 return typeManager.isDataType(type); 2887 } 2888 2889 public boolean isTlogging() { 2890 return tlogging; 2891 } 2892 2893 public void setTlogging(boolean tlogging) { 2894 this.tlogging = tlogging; 2895 } 2896 2897 public UcumService getUcumService() { 2898 return ucumService; 2899 } 2900 2901 public void setUcumService(UcumService ucumService) { 2902 this.ucumService = ucumService; 2903 } 2904 2905 public String getLinkForUrl(String corePath, String url) { 2906 if (url == null) { 2907 return null; 2908 } 2909 2910 if (codeSystems.has(url)) { 2911 return codeSystems.get(url).getWebPath(); 2912 } 2913 2914 if (valueSets.has(url)) { 2915 return valueSets.get(url).getWebPath(); 2916 } 2917 2918 if (maps.has(url)) { 2919 return maps.get(url).getWebPath(); 2920 } 2921 2922 if (transforms.has(url)) { 2923 return transforms.get(url).getWebPath(); 2924 } 2925 2926 if (actors.has(url)) { 2927 return actors.get(url).getWebPath(); 2928 } 2929 2930 if (requirements.has(url)) { 2931 return requirements.get(url).getWebPath(); 2932 } 2933 2934 if (structures.has(url)) { 2935 return structures.get(url).getWebPath(); 2936 } 2937 2938 if (guides.has(url)) { 2939 return guides.get(url).getWebPath(); 2940 } 2941 2942 if (capstmts.has(url)) { 2943 return capstmts.get(url).getWebPath(); 2944 } 2945 2946 if (measures.has(url)) { 2947 return measures.get(url).getWebPath(); 2948 } 2949 2950 if (libraries.has(url)) { 2951 return libraries.get(url).getWebPath(); 2952 } 2953 2954 if (searchParameters.has(url)) { 2955 return searchParameters.get(url).getWebPath(); 2956 } 2957 2958 if (questionnaires.has(url)) { 2959 return questionnaires.get(url).getWebPath(); 2960 } 2961 2962 if (operations.has(url)) { 2963 return operations.get(url).getWebPath(); 2964 } 2965 2966 if (plans.has(url)) { 2967 return plans.get(url).getWebPath(); 2968 } 2969 2970 if (url.equals("http://loinc.org")) { 2971 return corePath+"loinc.html"; 2972 } 2973 if (url.equals("http://unitsofmeasure.org")) { 2974 return corePath+"ucum.html"; 2975 } 2976 if (url.equals("http://snomed.info/sct")) { 2977 return corePath+"snomed.html"; 2978 } 2979 return null; 2980 } 2981 2982 public List<ImplementationGuide> allImplementationGuides() { 2983 List<ImplementationGuide> res = new ArrayList<>(); 2984 guides.listAll(res); 2985 return res; 2986 } 2987 2988 @Override 2989 public Set<String> getBinaryKeysAsSet() { return binaries.keySet(); } 2990 2991 @Override 2992 public boolean hasBinaryKey(String binaryKey) { 2993 return binaries.containsKey(binaryKey); 2994 } 2995 2996 @Override 2997 public byte[] getBinaryForKey(String binaryKey) { 2998 return binaries.get(binaryKey); 2999 } 3000 3001 public void finishLoading(boolean genSnapshots) { 3002 if (!hasResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Base")) { 3003 cacheResource(ProfileUtilities.makeBaseDefinition(version)); 3004 } 3005 if(genSnapshots) { 3006 for (StructureDefinition sd : listStructures()) { 3007 try { 3008 if (sd.getSnapshot().isEmpty()) { 3009 new ContextUtilities(this).generateSnapshot(sd); 3010 // new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd); 3011 } 3012 } catch (Exception e) { 3013 System.out.println("Unable to generate snapshot @1 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage()); 3014 if (logger.isDebugLogging()) { 3015 e.printStackTrace(); 3016 } 3017 } 3018 } 3019 } 3020 3021 codeSystems.setVersion(version); 3022 valueSets.setVersion(version); 3023 maps.setVersion(version); 3024 transforms.setVersion(version); 3025 structures.setVersion(version); 3026 typeManager.reload(); 3027 measures.setVersion(version); 3028 libraries.setVersion(version); 3029 guides.setVersion(version); 3030 capstmts.setVersion(version); 3031 searchParameters.setVersion(version); 3032 questionnaires.setVersion(version); 3033 operations.setVersion(version); 3034 plans.setVersion(version); 3035 systems.setVersion(version); 3036 actors.setVersion(version); 3037 requirements.setVersion(version); 3038 } 3039 3040 protected String tail(String url) { 3041 if (Utilities.noString(url)) { 3042 return "noname"; 3043 } 3044 if (url.contains("/")) { 3045 return url.substring(url.lastIndexOf("/")+1); 3046 } 3047 return url; 3048 } 3049 3050 public int getClientRetryCount() { 3051 return terminologyClientManager.getRetryCount(); 3052 } 3053 3054 public IWorkerContext setClientRetryCount(int value) { 3055 terminologyClientManager.setRetryCount(value); 3056 return this; 3057 } 3058 3059 public TerminologyClientManager getTxClientManager() { 3060 return terminologyClientManager; 3061 } 3062 3063 public String getCacheId() { 3064 return terminologyClientManager.getCacheId(); 3065 } 3066 3067 public TimeTracker clock() { 3068 return clock; 3069 } 3070 3071 public int countAllCaches() { 3072 return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() + 3073 guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() + 3074 systems.size()+ actors.size()+ requirements.size(); 3075 } 3076 3077 public Set<String> getCodeSystemsUsed() { 3078 return codeSystemsUsed ; 3079 } 3080 3081 public IWorkerContextManager.ICanonicalResourceLocator getLocator() { 3082 return locator; 3083 } 3084 3085 public void setLocator(IWorkerContextManager.ICanonicalResourceLocator locator) { 3086 this.locator = locator; 3087 } 3088 3089 public String getUserAgent() { 3090 return userAgent; 3091 } 3092 3093 protected void setUserAgent(String userAgent) { 3094 this.userAgent = userAgent; 3095 terminologyClientManager.setUserAgent(userAgent); 3096 } 3097 3098 3099 public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() { 3100 return packageTracker; 3101 } 3102 3103 public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker packageTracker) { 3104 this.packageTracker = packageTracker; 3105 return this; 3106 } 3107 3108 3109 @Override 3110 public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps) { 3111 // TODO Auto-generated method stub 3112 return new PEBuilder(this, elementProps, fixedProps); 3113 } 3114 3115 public boolean isForPublication() { 3116 return forPublication; 3117 } 3118 3119 public void setForPublication(boolean value) { 3120 forPublication = value; 3121 } 3122 3123 public boolean isCachingAllowed() { 3124 return cachingAllowed; 3125 } 3126 3127 public void setCachingAllowed(boolean cachingAllowed) { 3128 this.cachingAllowed = cachingAllowed; 3129 } 3130 3131 @Override 3132 public OIDSummary urlsForOid(String oid, String resourceType) { 3133 OIDSummary set = urlsForOid(oid, resourceType, true); 3134 if (set.getDefinitions().size() > 1) { 3135 set = urlsForOid(oid, resourceType, false); 3136 } 3137 return set; 3138 } 3139 3140 public OIDSummary urlsForOid(String oid, String resourceType, boolean retired) { 3141 OIDSummary summary = new OIDSummary(); 3142 if (oid != null) { 3143 if (oidCacheManual.containsKey(oid)) { 3144 summary.addOIDs(oidCacheManual.get(oid)); 3145 } 3146 for (OIDSource os : oidSources) { 3147 if (os.db == null) { 3148 os.db = connectToOidSource(os.folder); 3149 } 3150 if (os.db != null) { 3151 try { 3152 PreparedStatement psql = resourceType == null ? 3153 os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where OID = ?") : 3154 os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where TYPE = '"+resourceType+"' and OID = ?"); 3155 psql.setString(1, oid); 3156 ResultSet rs = psql.executeQuery(); 3157 while (rs.next()) { 3158 if (retired || !"retired".equals(rs.getString(4))) { 3159 String rt = rs.getString(1); 3160 String url = rs.getString(2); 3161 String version = rs.getString(3); 3162 summary.addOID(new OIDDefinition(rt, oid, url, version, os.pid)); 3163 } 3164 } 3165 } catch (Exception e) { 3166 // nothing, there would alreagy have been an error 3167 // e.printStackTrace(); 3168 } 3169 } 3170 } 3171 3172 switch (oid) { 3173 case "2.16.840.1.113883.6.1" : 3174 summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.1", "http://loinc.org", null, null)); 3175 break; 3176 case "2.16.840.1.113883.6.8" : 3177 summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.8", "http://unitsofmeasure.org", null, null)); 3178 break; 3179 case "2.16.840.1.113883.6.96" : 3180 summary.addOID(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.96", "http://snomed.info/sct", null, null)); 3181 break; 3182 default: 3183 } 3184 } 3185 summary.sort(); 3186 return summary; 3187 } 3188 3189 private Connection connectToOidSource(String folder) { 3190 try { 3191 File ff = ManagedFileAccess.file(folder); 3192 File of = ManagedFileAccess.file(Utilities.path(ff.getAbsolutePath(), ".oid-map-2.db")); 3193 if (!of.exists()) { 3194 OidIndexBuilder oidBuilder = new OidIndexBuilder(ff, of); 3195 oidBuilder.build(); 3196 } 3197 return DriverManager.getConnection("jdbc:sqlite:"+of.getAbsolutePath()); 3198 } catch (Exception e) { 3199 return null; 3200 } 3201 } 3202 3203 3204 public void unload() { 3205 3206 codeSystems.unload(); 3207 valueSets.unload(); 3208 maps.unload(); 3209 transforms.unload(); 3210 structures.unload(); 3211 typeManager.unload(); 3212 measures.unload(); 3213 libraries.unload(); 3214 guides.unload(); 3215 capstmts.unload(); 3216 searchParameters.unload(); 3217 questionnaires.unload(); 3218 operations.unload(); 3219 plans.unload(); 3220 actors.unload(); 3221 requirements.unload(); 3222 systems.unload(); 3223 3224 binaries.clear(); 3225 validationCache.clear(); 3226 txCache.unload(); 3227} 3228 3229 private <T extends Resource> T doFindTxResource(Class<T> class_, String canonical) { 3230 // well, we haven't found it locally. We're going look it up 3231 if (class_ == ValueSet.class) { 3232 SourcedValueSet svs = null; 3233 if (txCache.hasValueSet(canonical)) { 3234 svs = txCache.getValueSet(canonical); 3235 } else { 3236 svs = terminologyClientManager.findValueSetOnServer(canonical); 3237 txCache.cacheValueSet(canonical, svs); 3238 } 3239 if (svs != null) { 3240 String web = ToolingExtensions.readStringExtension(svs.getVs(), ToolingExtensions.EXT_WEB_SOURCE); 3241 if (web == null) { 3242 web = Utilities.pathURL(svs.getServer(), "ValueSet", svs.getVs().getIdBase()); 3243 } 3244 svs.getVs().setWebPath(web); 3245 svs.getVs().setUserData("External.Link", svs.getServer()); // so we can render it differently 3246 } 3247 if (svs == null) { 3248 return null; 3249 } else { 3250 cacheResource(svs.getVs()); 3251 return (T) svs.getVs(); 3252 } 3253 } else if (class_ == CodeSystem.class) { 3254 SourcedCodeSystem scs = null; 3255 if (txCache.hasCodeSystem(canonical)) { 3256 scs = txCache.getCodeSystem(canonical); 3257 } else { 3258 scs = terminologyClientManager.findCodeSystemOnServer(canonical); 3259 txCache.cacheCodeSystem(canonical, scs); 3260 } 3261 if (scs != null) { 3262 String web = ToolingExtensions.readStringExtension(scs.getCs(), ToolingExtensions.EXT_WEB_SOURCE); 3263 if (web == null) { 3264 web = Utilities.pathURL(scs.getServer(), "ValueSet", scs.getCs().getIdBase()); 3265 } 3266 scs.getCs().setWebPath(web); 3267 scs.getCs().setUserData("External.Link", scs.getServer()); // so we can render it differently 3268 } 3269 if (scs == null) { 3270 return null; 3271 } else { 3272 cacheResource(scs.getCs()); 3273 return (T) scs.getCs(); 3274 } 3275 } else { 3276 throw new Error("Not supported"); 3277 } 3278 } 3279 3280 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, Resource sourceOfReference) { 3281 if (canonical == null) { 3282 return null; 3283 } 3284 T result = fetchResource(class_, canonical, sourceOfReference); 3285 if (result == null) { 3286 result = doFindTxResource(class_, canonical); 3287 } 3288 return result; 3289 } 3290 3291 public <T extends Resource> T findTxResource(Class<T> class_, String canonical) { 3292 if (canonical == null) { 3293 return null; 3294 } 3295 T result = fetchResource(class_, canonical); 3296 if (result == null) { 3297 result = doFindTxResource(class_, canonical); 3298 } 3299 return result; 3300 } 3301 3302 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version) { 3303 if (canonical == null) { 3304 return null; 3305 } 3306 T result = fetchResource(class_, canonical, version); 3307 if (result == null) { 3308 result = doFindTxResource(class_, canonical+"|"+version); 3309 } 3310 return result; 3311 } 3312 3313 @Override 3314 public <T extends Resource> List<T> fetchResourcesByUrl(Class<T> class_, String uri) { 3315 List<T> res = new ArrayList<>(); 3316 if (uri != null && !uri.startsWith("#")) { 3317 if (class_ == StructureDefinition.class) { 3318 uri = ProfileUtilities.sdNs(uri, null); 3319 } 3320 assert !uri.contains("|"); 3321 if (uri.contains("#")) { 3322 uri = uri.substring(0, uri.indexOf("#")); 3323 } 3324 synchronized (lock) { 3325 if (class_ == Resource.class || class_ == null) { 3326 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 3327 for (ResourceProxy r : rt.values()) { 3328 if (uri.equals(r.getUrl())) { 3329 res.add((T) r.getResource()); 3330 } 3331 } 3332 } 3333 } 3334 if (class_ == ImplementationGuide.class || class_ == Resource.class || class_ == null) { 3335 for (ImplementationGuide cr : guides.getForUrl(uri)) { 3336 res.add((T) cr); 3337 } 3338 } else if (class_ == CapabilityStatement.class || class_ == Resource.class || class_ == null) { 3339 for (CapabilityStatement cr : capstmts.getForUrl(uri)) { 3340 res.add((T) cr); 3341 } 3342 } else if (class_ == Measure.class || class_ == Resource.class || class_ == null) { 3343 for (Measure cr : measures.getForUrl(uri)) { 3344 res.add((T) cr); 3345 } 3346 } else if (class_ == Library.class || class_ == Resource.class || class_ == null) { 3347 for (Library cr : libraries.getForUrl(uri)) { 3348 res.add((T) cr); 3349 } 3350 } else if (class_ == StructureDefinition.class || class_ == Resource.class || class_ == null) { 3351 for (StructureDefinition cr : structures.getForUrl(uri)) { 3352 res.add((T) cr); 3353 } 3354 } else if (class_ == StructureMap.class || class_ == Resource.class || class_ == null) { 3355 for (StructureMap cr : transforms.getForUrl(uri)) { 3356 res.add((T) cr); 3357 } 3358 } else if (class_ == NamingSystem.class || class_ == Resource.class || class_ == null) { 3359 for (NamingSystem cr : systems.getForUrl(uri)) { 3360 res.add((T) cr); 3361 } 3362 } else if (class_ == ValueSet.class || class_ == Resource.class || class_ == null) { 3363 for (ValueSet cr : valueSets.getForUrl(uri)) { 3364 res.add((T) cr); 3365 } 3366 } else if (class_ == CodeSystem.class || class_ == Resource.class || class_ == null) { 3367 for (CodeSystem cr : codeSystems.getForUrl(uri)) { 3368 res.add((T) cr); 3369 } 3370 } else if (class_ == ConceptMap.class || class_ == Resource.class || class_ == null) { 3371 for (ConceptMap cr : maps.getForUrl(uri)) { 3372 res.add((T) cr); 3373 } 3374 } else if (class_ == ActorDefinition.class || class_ == Resource.class || class_ == null) { 3375 for (ActorDefinition cr : actors.getForUrl(uri)) { 3376 res.add((T) cr); 3377 } 3378 } else if (class_ == Requirements.class || class_ == Resource.class || class_ == null) { 3379 for (Requirements cr : requirements.getForUrl(uri)) { 3380 res.add((T) cr); 3381 } 3382 } else if (class_ == PlanDefinition.class || class_ == Resource.class || class_ == null) { 3383 for (PlanDefinition cr : plans.getForUrl(uri)) { 3384 res.add((T) cr); 3385 } 3386 } else if (class_ == OperationDefinition.class || class_ == Resource.class || class_ == null) { 3387 for (OperationDefinition cr : operations.getForUrl(uri)) { 3388 res.add((T) cr); 3389 } 3390 } else if (class_ == Questionnaire.class || class_ == Resource.class || class_ == null) { 3391 for (Questionnaire cr : questionnaires.getForUrl(uri)) { 3392 res.add((T) cr); 3393 } 3394 } else if (class_ == SearchParameter.class || class_ == Resource.class || class_ == null) { 3395 for (SearchParameter cr : searchParameters.getForUrl(uri)) { 3396 res.add((T) cr); 3397 } 3398 } 3399 } 3400 } 3401 return res; 3402 } 3403 3404}