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