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.util.ArrayList; 038import java.util.Collections; 039import java.util.Comparator; 040import java.util.Date; 041import java.util.HashMap; 042import java.util.HashSet; 043import java.util.List; 044import java.util.Locale; 045import java.util.Map; 046import java.util.Set; 047 048import lombok.Getter; 049import org.apache.commons.lang3.StringUtils; 050import org.fhir.ucum.UcumService; 051import org.hl7.fhir.exceptions.DefinitionException; 052import org.hl7.fhir.exceptions.FHIRException; 053import org.hl7.fhir.exceptions.NoTerminologyServiceException; 054import org.hl7.fhir.exceptions.TerminologyServiceException; 055import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 056import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; 057import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; 058import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; 059import org.hl7.fhir.r5.model.ActorDefinition; 060import org.hl7.fhir.r5.model.BooleanType; 061import org.hl7.fhir.r5.model.Bundle; 062import org.hl7.fhir.r5.model.CanonicalResource; 063import org.hl7.fhir.r5.model.CanonicalType; 064import org.hl7.fhir.r5.model.CapabilityStatement; 065import org.hl7.fhir.r5.model.CodeSystem; 066import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode; 067import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 068import org.hl7.fhir.r5.model.CodeableConcept; 069import org.hl7.fhir.r5.model.Coding; 070import org.hl7.fhir.r5.model.ConceptMap; 071import org.hl7.fhir.r5.model.Constants; 072import org.hl7.fhir.r5.model.DomainResource; 073import org.hl7.fhir.r5.model.ElementDefinition; 074import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 075import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 076import org.hl7.fhir.r5.model.IdType; 077import org.hl7.fhir.r5.model.ImplementationGuide; 078import org.hl7.fhir.r5.model.Library; 079import org.hl7.fhir.r5.model.Measure; 080import org.hl7.fhir.r5.model.NamingSystem; 081import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType; 082import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent; 083import org.hl7.fhir.r5.model.OperationDefinition; 084import org.hl7.fhir.r5.model.OperationOutcome; 085import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent; 086import org.hl7.fhir.r5.model.PackageInformation; 087import org.hl7.fhir.r5.model.Parameters; 088import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; 089import org.hl7.fhir.r5.model.PlanDefinition; 090import org.hl7.fhir.r5.model.PrimitiveType; 091import org.hl7.fhir.r5.model.Questionnaire; 092import org.hl7.fhir.r5.model.Requirements; 093import org.hl7.fhir.r5.model.Resource; 094import org.hl7.fhir.r5.model.SearchParameter; 095import org.hl7.fhir.r5.model.StringType; 096import org.hl7.fhir.r5.model.StructureDefinition; 097import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 098import org.hl7.fhir.r5.model.StructureMap; 099import org.hl7.fhir.r5.model.TerminologyCapabilities; 100import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent; 101import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesExpansionParameterComponent; 102import org.hl7.fhir.r5.model.UriType; 103import org.hl7.fhir.r5.model.ValueSet; 104import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; 105import org.hl7.fhir.r5.model.Bundle.BundleType; 106import org.hl7.fhir.r5.model.Bundle.HTTPVerb; 107import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 108import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; 109import org.hl7.fhir.r5.profilemodel.PEDefinition; 110import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; 111import org.hl7.fhir.r5.profilemodel.PEBuilder; 112import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer; 113import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 114import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander; 115import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 116import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext; 117import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException; 118import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; 119import org.hl7.fhir.r5.terminologies.validation.VSCheckerException; 120import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator; 121import org.hl7.fhir.r5.terminologies.ValueSetUtilities; 122import org.hl7.fhir.r5.terminologies.client.ITerminologyClient; 123import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext; 124import org.hl7.fhir.r5.utils.PackageHackerR5; 125import org.hl7.fhir.r5.utils.ResourceUtilities; 126import org.hl7.fhir.r5.utils.ToolingExtensions; 127import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; 128import org.hl7.fhir.utilities.TimeTracker; 129import org.hl7.fhir.utilities.ToolingClientLogger; 130import org.hl7.fhir.utilities.TranslationServices; 131import org.hl7.fhir.utilities.Utilities; 132import org.hl7.fhir.utilities.VersionUtilities; 133import org.hl7.fhir.utilities.i18n.I18nBase; 134import org.hl7.fhir.utilities.i18n.I18nConstants; 135import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 136import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 137import org.hl7.fhir.utilities.validation.ValidationOptions; 138import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode; 139 140import com.google.gson.JsonObject; 141 142import javax.annotation.Nonnull; 143 144public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{ 145 146 private static final boolean QA_CHECK_REFERENCE_SOURCE = false; // see comments below 147 148 public class ResourceProxy { 149 private Resource resource; 150 private CanonicalResourceProxy proxy; 151 152 public ResourceProxy(Resource resource) { 153 super(); 154 this.resource = resource; 155 } 156 public ResourceProxy(CanonicalResourceProxy proxy) { 157 super(); 158 this.proxy = proxy; 159 } 160 161 public Resource getResource() { 162 return resource != null ? resource : proxy.getResource(); 163 } 164 165 public CanonicalResourceProxy getProxy() { 166 return proxy; 167 } 168 169 public String getUrl() { 170 if (resource == null) { 171 return proxy.getUrl(); 172 } else if (resource instanceof CanonicalResource) { 173 return ((CanonicalResource) resource).getUrl(); 174 } else { 175 return null; 176 } 177 } 178 179 } 180 181 public class MetadataResourceVersionComparator<T extends CanonicalResource> implements Comparator<T> { 182 183 final private List<T> list; 184 185 public MetadataResourceVersionComparator(List<T> list) { 186 this.list = list; 187 } 188 189 @Override 190 public int compare(T arg1, T arg2) { 191 String v1 = arg1.getVersion(); 192 String v2 = arg2.getVersion(); 193 if (v1 == null && v2 == null) { 194 return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order 195 } else if (v1 == null) { 196 return -1; 197 } else if (v2 == null) { 198 return 1; 199 } else { 200 String mm1 = VersionUtilities.getMajMin(v1); 201 String mm2 = VersionUtilities.getMajMin(v2); 202 if (mm1 == null || mm2 == null) { 203 return v1.compareTo(v2); 204 } else { 205 return mm1.compareTo(mm2); 206 } 207 } 208 } 209 } 210 211 private Object lock = new Object(); // used as a lock for the data that follows 212 protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be 213 214 protected TerminologyClientContext tcc = new TerminologyClientContext(); 215 private boolean minimalMemory = false; 216 217 private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>(); 218 // all maps are to the full URI 219 private CanonicalResourceManager<CodeSystem> codeSystems = new CanonicalResourceManager<CodeSystem>(false, minimalMemory); 220 private final Set<String> supportedCodeSystems = new HashSet<String>(); 221 private final Set<String> unsupportedCodeSystems = new HashSet<String>(); // know that the terminology server doesn't support them 222 private CanonicalResourceManager<ValueSet> valueSets = new CanonicalResourceManager<ValueSet>(false, minimalMemory); 223 private CanonicalResourceManager<ConceptMap> maps = new CanonicalResourceManager<ConceptMap>(false, minimalMemory); 224 protected CanonicalResourceManager<StructureMap> transforms = new CanonicalResourceManager<StructureMap>(false, minimalMemory); 225 private CanonicalResourceManager<StructureDefinition> structures = new CanonicalResourceManager<StructureDefinition>(false, minimalMemory); 226 private final CanonicalResourceManager<Measure> measures = new CanonicalResourceManager<Measure>(false, minimalMemory); 227 private final CanonicalResourceManager<Library> libraries = new CanonicalResourceManager<Library>(false, minimalMemory); 228 private CanonicalResourceManager<ImplementationGuide> guides = new CanonicalResourceManager<ImplementationGuide>(false, minimalMemory); 229 private final CanonicalResourceManager<CapabilityStatement> capstmts = new CanonicalResourceManager<CapabilityStatement>(false, minimalMemory); 230 private final CanonicalResourceManager<SearchParameter> searchParameters = new CanonicalResourceManager<SearchParameter>(false, minimalMemory); 231 private final CanonicalResourceManager<Questionnaire> questionnaires = new CanonicalResourceManager<Questionnaire>(false, minimalMemory); 232 private final CanonicalResourceManager<OperationDefinition> operations = new CanonicalResourceManager<OperationDefinition>(false, minimalMemory); 233 private final CanonicalResourceManager<PlanDefinition> plans = new CanonicalResourceManager<PlanDefinition>(false, minimalMemory); 234 private final CanonicalResourceManager<ActorDefinition> actors = new CanonicalResourceManager<ActorDefinition>(false, minimalMemory); 235 private final CanonicalResourceManager<Requirements> requirements = new CanonicalResourceManager<Requirements>(false, minimalMemory); 236 private final CanonicalResourceManager<NamingSystem> systems = new CanonicalResourceManager<NamingSystem>(false, minimalMemory); 237 private Map<String, NamingSystem> systemUrlMap; 238 239 240 private UcumService ucumService; 241 protected Map<String, byte[]> binaries = new HashMap<String, byte[]>(); 242 protected Map<String, String> oidCache = new HashMap<>(); 243 244 protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String,ValidationResult>>(); 245 protected String name; 246 private boolean allowLoadingDuplicates; 247 248 private final Set<String> codeSystemsUsed = new HashSet<>(); 249 protected ToolingClientLogger txLog; 250 private boolean canRunWithoutTerminology; 251 protected boolean noTerminologyServer; 252 private int expandCodesLimit = 1000; 253 protected ILoggingService logger = new SystemOutLoggingService(); 254 protected Parameters expParameters; 255 private TranslationServices translator = new NullTranslator(); 256 private Map<String, PackageInformation> packages = new HashMap<>(); 257 258 @Getter 259 protected TerminologyCache txCache; 260 protected TimeTracker clock; 261 private boolean tlogging = true; 262 private IWorkerContextManager.ICanonicalResourceLocator locator; 263 protected String userAgent; 264 265 protected BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException { 266 setValidationMessageLanguage(getLocale()); 267 clock = new TimeTracker(); 268 } 269 270 protected BaseWorkerContext(Locale locale) throws FileNotFoundException, IOException, FHIRException { 271 setValidationMessageLanguage(locale); 272 clock = new TimeTracker(); 273 } 274 275 protected BaseWorkerContext(CanonicalResourceManager<CodeSystem> codeSystems, CanonicalResourceManager<ValueSet> valueSets, CanonicalResourceManager<ConceptMap> maps, CanonicalResourceManager<StructureDefinition> profiles, 276 CanonicalResourceManager<ImplementationGuide> guides) throws FileNotFoundException, IOException, FHIRException { 277 this(); 278 this.codeSystems = codeSystems; 279 this.valueSets = valueSets; 280 this.maps = maps; 281 this.structures = profiles; 282 this.guides = guides; 283 clock = new TimeTracker(); 284 } 285 286 protected void copy(BaseWorkerContext other) { 287 synchronized (other.lock) { // tricky, because you need to lock this as well, but it's really not in use yet 288 allResourcesById.putAll(other.allResourcesById); 289 translator = other.translator; 290 codeSystems.copy(other.codeSystems); 291 valueSets.copy(other.valueSets); 292 maps.copy(other.maps); 293 transforms.copy(other.transforms); 294 structures.copy(other.structures); 295 searchParameters.copy(other.searchParameters); 296 plans.copy(other.plans); 297 questionnaires.copy(other.questionnaires); 298 operations.copy(other.operations); 299 systems.copy(other.systems); 300 systemUrlMap = null; 301 guides.copy(other.guides); 302 capstmts.copy(other.capstmts); 303 measures.copy(other.measures); 304 libraries.copy(libraries); 305 306 allowLoadingDuplicates = other.allowLoadingDuplicates; 307 name = other.name; 308 txLog = other.txLog; 309 canRunWithoutTerminology = other.canRunWithoutTerminology; 310 noTerminologyServer = other.noTerminologyServer; 311 if (other.txCache != null) 312 txCache = other.txCache; // no copy. for now? 313 expandCodesLimit = other.expandCodesLimit; 314 logger = other.logger; 315 expParameters = other.expParameters; 316 version = other.version; 317 supportedCodeSystems.addAll(other.supportedCodeSystems); 318 unsupportedCodeSystems.addAll(other.unsupportedCodeSystems); 319 codeSystemsUsed.addAll(other.codeSystemsUsed); 320 ucumService = other.ucumService; 321 binaries.putAll(other.binaries); 322 oidCache.putAll(other.oidCache); 323 validationCache.putAll(other.validationCache); 324 tlogging = other.tlogging; 325 locator = other.locator; 326 userAgent = other.userAgent; 327 tcc.copy(other.tcc); 328 cachingAllowed = other.cachingAllowed; 329 } 330 } 331 332 333 public void cacheResource(Resource r) throws FHIRException { 334 cacheResourceFromPackage(r, null); 335 } 336 337 338 public void registerResourceFromPackage(CanonicalResourceProxy r, PackageInformation packageInfo) throws FHIRException { 339 PackageHackerR5.fixLoadedResource(r, packageInfo); 340 341 synchronized (lock) { 342 if (packageInfo != null) { 343 packages.put(packageInfo.getVID(), packageInfo); 344 } 345 if (r.getId() != null) { 346 Map<String, ResourceProxy> map = allResourcesById.get(r.getType()); 347 if (map == null) { 348 map = new HashMap<String, ResourceProxy>(); 349 allResourcesById.put(r.getType(), map); 350 } 351 if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) { 352 map.put(r.getId(), new ResourceProxy(r)); 353 } 354 } 355 356 String url = r.getUrl(); 357 if (!allowLoadingDuplicates && hasResourceVersion(r.getType(), url, r.getVersion()) && !packageInfo.isHTO()) { 358 // spcial workaround for known problems with existing packages 359 if (Utilities.existsInList(url, "http://hl7.org/fhir/SearchParameter/example")) { 360 return; 361 } 362 CanonicalResource ex = fetchResourceWithException(r.getType(), url); 363 throw new DefinitionException(formatMessage(I18nConstants.DUPLICATE_RESOURCE_, url, r.getVersion(), ex.getVersion(), 364 ex.fhirType())); 365 } 366 switch(r.getType()) { 367 case "StructureDefinition": 368 if ("1.4.0".equals(version)) { 369 StructureDefinition sd = (StructureDefinition) r.getResource(); 370 fixOldSD(sd); 371 } 372 structures.register(r, packageInfo); 373 break; 374 case "ValueSet": 375 valueSets.register(r, packageInfo); 376 break; 377 case "CodeSystem": 378 codeSystems.register(r, packageInfo); 379 break; 380 case "ImplementationGuide": 381 guides.register(r, packageInfo); 382 break; 383 case "CapabilityStatement": 384 capstmts.register(r, packageInfo); 385 break; 386 case "Measure": 387 measures.register(r, packageInfo); 388 break; 389 case "Library": 390 libraries.register(r, packageInfo); 391 break; 392 case "SearchParameter": 393 searchParameters.register(r, packageInfo); 394 break; 395 case "PlanDefinition": 396 plans.register(r, packageInfo); 397 break; 398 case "OperationDefinition": 399 operations.register(r, packageInfo); 400 break; 401 case "Questionnaire": 402 questionnaires.register(r, packageInfo); 403 break; 404 case "ConceptMap": 405 maps.register(r, packageInfo); 406 break; 407 case "StructureMap": 408 transforms.register(r, packageInfo); 409 break; 410 case "NamingSystem": 411 systems.register(r, packageInfo); 412 break; 413 case "Requirements": 414 requirements.register(r, packageInfo); 415 break; 416 case "ActorDefinition": 417 actors.register(r, packageInfo); 418 break; 419 } 420 } 421 } 422 423 public void cacheResourceFromPackage(Resource r, PackageInformation packageInfo) throws FHIRException { 424 425 synchronized (lock) { 426 if (packageInfo != null) { 427 packages.put(packageInfo.getVID(), packageInfo); 428 } 429 430 if (r.getId() != null) { 431 Map<String, ResourceProxy> map = allResourcesById.get(r.fhirType()); 432 if (map == null) { 433 map = new HashMap<String, ResourceProxy>(); 434 allResourcesById.put(r.fhirType(), map); 435 } 436 if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) { 437 map.put(r.getId(), new ResourceProxy(r)); 438 } else { 439 logger.logDebugMessage(LogCategory.PROGRESS,"Ignore "+r.fhirType()+"/"+r.getId()+" from package "+packageInfo.toString()); 440 } 441 } 442 443 if (r instanceof CodeSystem || r instanceof NamingSystem) { 444 oidCache.clear(); 445 } 446 447 if (r instanceof CanonicalResource) { 448 CanonicalResource m = (CanonicalResource) r; 449 String url = m.getUrl(); 450 if (!allowLoadingDuplicates && hasResource(r.getClass(), url)) { 451 // special workaround for known problems with existing packages 452 if (Utilities.existsInList(url, "http://hl7.org/fhir/SearchParameter/example")) { 453 return; 454 } 455 CanonicalResource ex = (CanonicalResource) fetchResourceWithException(r.getClass(), url); 456 throw new DefinitionException(formatMessage(I18nConstants.DUPLICATE_RESOURCE_, url, ((CanonicalResource) r).getVersion(), ex.getVersion(), 457 ex.fhirType())); 458 } 459 if (r instanceof StructureDefinition) { 460 StructureDefinition sd = (StructureDefinition) m; 461 if ("1.4.0".equals(version)) { 462 fixOldSD(sd); 463 } 464 structures.see(sd, packageInfo); 465 } else if (r instanceof ValueSet) { 466 valueSets.see((ValueSet) m, packageInfo); 467 } else if (r instanceof CodeSystem) { 468 CodeSystemUtilities.crossLinkCodeSystem((CodeSystem) r); 469 codeSystems.see((CodeSystem) m, packageInfo); 470 } else if (r instanceof ImplementationGuide) { 471 guides.see((ImplementationGuide) m, packageInfo); 472 } else if (r instanceof CapabilityStatement) { 473 capstmts.see((CapabilityStatement) m, packageInfo); 474 } else if (r instanceof Measure) { 475 measures.see((Measure) m, packageInfo); 476 } else if (r instanceof Library) { 477 libraries.see((Library) m, packageInfo); 478 } else if (r instanceof SearchParameter) { 479 searchParameters.see((SearchParameter) m, packageInfo); 480 } else if (r instanceof PlanDefinition) { 481 plans.see((PlanDefinition) m, packageInfo); 482 } else if (r instanceof OperationDefinition) { 483 operations.see((OperationDefinition) m, packageInfo); 484 } else if (r instanceof Questionnaire) { 485 questionnaires.see((Questionnaire) m, packageInfo); 486 } else if (r instanceof ConceptMap) { 487 maps.see((ConceptMap) m, packageInfo); 488 } else if (r instanceof StructureMap) { 489 transforms.see((StructureMap) m, packageInfo); 490 } else if (r instanceof NamingSystem) { 491 systems.see((NamingSystem) m, packageInfo); 492 systemUrlMap = null; 493 } else if (r instanceof Requirements) { 494 requirements.see((Requirements) m, packageInfo); 495 } else if (r instanceof ActorDefinition) { 496 actors.see((ActorDefinition) m, packageInfo); 497 systemUrlMap = null; 498 } 499 } 500 } 501 } 502 503 public Map<String, NamingSystem> getNSUrlMap() { 504 if (systemUrlMap == null) { 505 systemUrlMap = new HashMap<>(); 506 List<NamingSystem> nsl = systems.getList(); 507 for (NamingSystem ns : nsl) { 508 for (NamingSystemUniqueIdComponent uid : ns.getUniqueId()) { 509 if (uid.getType() == NamingSystemIdentifierType.URI && uid.hasValue()) { 510 systemUrlMap.put(uid.getValue(), ns) ; 511 } 512 } 513 } 514 } 515 return systemUrlMap; 516 } 517 518 519 public void fixOldSD(StructureDefinition sd) { 520 if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && sd.getType().equals("Extension") && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { 521 sd.setSnapshot(null); 522 } 523 for (ElementDefinition ed : sd.getDifferential().getElement()) { 524 if (ed.getPath().equals("Extension.url") || ed.getPath().endsWith(".extension.url") ) { 525 ed.setMin(1); 526 if (ed.hasBase()) { 527 ed.getBase().setMin(1); 528 } 529 } 530 if ("extension".equals(ed.getSliceName())) { 531 ed.setSliceName(null); 532 } 533 } 534 } 535 536 /* 537 * Compare business versions, returning "true" if the candidate newer version is in fact newer than the oldVersion 538 * Comparison will work for strictly numeric versions as well as multi-level versions separated by ., -, _, : or space 539 * Failing that, it will do unicode-based character ordering. 540 * E.g. 1.5.3 < 1.14.3 541 * 2017-3-10 < 2017-12-7 542 * A3 < T2 543 */ 544 private boolean laterVersion(String newVersion, String oldVersion) { 545 // Compare business versions, retur 546 newVersion = newVersion.trim(); 547 oldVersion = oldVersion.trim(); 548 if (StringUtils.isNumeric(newVersion) && StringUtils.isNumeric(oldVersion)) { 549 return Double.parseDouble(newVersion) > Double.parseDouble(oldVersion); 550 } else if (hasDelimiter(newVersion, oldVersion, ".")) { 551 return laterDelimitedVersion(newVersion, oldVersion, "\\."); 552 } else if (hasDelimiter(newVersion, oldVersion, "-")) { 553 return laterDelimitedVersion(newVersion, oldVersion, "\\-"); 554 } else if (hasDelimiter(newVersion, oldVersion, "_")) { 555 return laterDelimitedVersion(newVersion, oldVersion, "\\_"); 556 } else if (hasDelimiter(newVersion, oldVersion, ":")) { 557 return laterDelimitedVersion(newVersion, oldVersion, "\\:"); 558 } else if (hasDelimiter(newVersion, oldVersion, " ")) { 559 return laterDelimitedVersion(newVersion, oldVersion, "\\ "); 560 } else { 561 return newVersion.compareTo(oldVersion) > 0; 562 } 563 } 564 565 /* 566 * Returns true if both strings include the delimiter and have the same number of occurrences of it 567 */ 568 private boolean hasDelimiter(String s1, String s2, String delimiter) { 569 return s1.contains(delimiter) && s2.contains(delimiter) && s1.split(delimiter).length == s2.split(delimiter).length; 570 } 571 572 private boolean laterDelimitedVersion(String newVersion, String oldVersion, String delimiter) { 573 String[] newParts = newVersion.split(delimiter); 574 String[] oldParts = oldVersion.split(delimiter); 575 for (int i = 0; i < newParts.length; i++) { 576 if (!newParts[i].equals(oldParts[i])) { 577 return laterVersion(newParts[i], oldParts[i]); 578 } 579 } 580 // This should never happen 581 throw new Error(formatMessage(I18nConstants.DELIMITED_VERSIONS_HAVE_EXACT_MATCH_FOR_DELIMITER____VS_, delimiter, newParts, oldParts)); 582 } 583 584 protected <T extends CanonicalResource> void seeMetadataResource(T r, Map<String, T> map, List<T> list, boolean addId) throws FHIRException { 585// if (addId) 586 // map.put(r.getId(), r); // todo: why? 587 list.add(r); 588 if (r.hasUrl()) { 589 // first, this is the correct reosurce for this version (if it has a version) 590 if (r.hasVersion()) { 591 map.put(r.getUrl()+"|"+r.getVersion(), r); 592 } 593 // if we haven't get anything for this url, it's the correct version 594 if (!map.containsKey(r.getUrl())) { 595 map.put(r.getUrl(), r); 596 } else { 597 List<T> rl = new ArrayList<T>(); 598 for (T t : list) { 599 if (t.getUrl().equals(r.getUrl()) && !rl.contains(t)) { 600 rl.add(t); 601 } 602 } 603 Collections.sort(rl, new MetadataResourceVersionComparator<T>(list)); 604 map.put(r.getUrl(), rl.get(rl.size()-1)); 605 T latest = null; 606 for (T t : rl) { 607 if (VersionUtilities.versionsCompatible(t.getVersion(), r.getVersion())) { 608 latest = t; 609 } 610 } 611 if (latest != null) { // might be null if it's not using semver 612 map.put(r.getUrl()+"|"+VersionUtilities.getMajMin(latest.getVersion()), rl.get(rl.size()-1)); 613 } 614 } 615 } 616 } 617 618 @Override 619 public CodeSystem fetchCodeSystem(String system) { 620 if (system == null) { 621 return null; 622 } 623 if (system.contains("|")) { 624 String s = system.substring(0, system.indexOf("|")); 625 String v = system.substring(system.indexOf("|")+1); 626 return fetchCodeSystem(s, v); 627 } 628 CodeSystem cs; 629 synchronized (lock) { 630 cs = codeSystems.get(system); 631 } 632 if (cs == null && locator != null) { 633 locator.findResource(this, system); 634 synchronized (lock) { 635 cs = codeSystems.get(system); 636 } 637 } 638 return cs; 639 } 640 641 public CodeSystem fetchCodeSystem(String system, String version) { 642 if (version == null) { 643 return fetchCodeSystem(system); 644 } 645 CodeSystem cs; 646 synchronized (lock) { 647 cs = codeSystems.get(system, version); 648 } 649 if (cs == null && locator != null) { 650 locator.findResource(this, system); 651 synchronized (lock) { 652 cs = codeSystems.get(system); 653 } 654 } 655 return cs; 656 } 657 658 @Override 659 public CodeSystem fetchSupplementedCodeSystem(String system) { 660 CodeSystem cs = fetchCodeSystem(system); 661 if (cs != null) { 662 List<CodeSystem> supplements = codeSystems.getSupplements(cs); 663 if (supplements.size() > 0) { 664 cs = CodeSystemUtilities.mergeSupplements(cs, supplements); 665 } 666 } 667 return cs; 668 } 669 670 @Override 671 public CodeSystem fetchSupplementedCodeSystem(String system, String version) { 672 CodeSystem cs = fetchCodeSystem(system, version); 673 if (cs != null) { 674 List<CodeSystem> supplements = codeSystems.getSupplements(cs); 675 if (supplements.size() > 0) { 676 cs = CodeSystemUtilities.mergeSupplements(cs, supplements); 677 } 678 } 679 return cs; 680 } 681 682 @Override 683 public boolean supportsSystem(String system) throws TerminologyServiceException { 684 synchronized (lock) { 685 if (codeSystems.has(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT) { 686 return true; 687 } else if (supportedCodeSystems.contains(system)) { 688 return true; 689 } else if (system.startsWith("http://example.org") || system.startsWith("http://acme.com") || system.startsWith("http://hl7.org/fhir/valueset-") || system.startsWith("urn:oid:")) { 690 return false; 691 } else { 692 if (noTerminologyServer) { 693 return false; 694 } 695 if (tcc.getTxcaps() == null) { 696 try { 697 logger.logMessage("Terminology server: Check for supported code systems for "+system); 698 final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getClient().getTerminologyCapabilities(); 699 txCache.cacheTerminologyCapabilities(capabilityStatement); 700 setTxCaps(capabilityStatement); 701 } catch (Exception e) { 702 if (canRunWithoutTerminology) { 703 noTerminologyServer = true; 704 logger.logMessage("==============!! Running without terminology server !! =============="); 705 if (tcc.getClient() != null) { 706 logger.logMessage("txServer = "+tcc.getClient().getId()); 707 logger.logMessage("Error = "+e.getMessage()+""); 708 } 709 logger.logMessage("====================================================================="); 710 return false; 711 } else { 712 e.printStackTrace(); 713 throw new TerminologyServiceException(e); 714 } 715 } 716 if (supportedCodeSystems.contains(system)) { 717 return true; 718 } 719 } 720 } 721 return false; 722 } 723 } 724 725 726 727 728 729 protected void txLog(String msg) { 730 if (tlogging ) { 731 logger.logDebugMessage(LogCategory.TX, msg); 732 } 733 } 734 735 // --- expansion support ------------------------------------------------------------------------------------------------------------ 736 737 public int getExpandCodesLimit() { 738 return expandCodesLimit; 739 } 740 741 public void setExpandCodesLimit(int expandCodesLimit) { 742 this.expandCodesLimit = expandCodesLimit; 743 } 744 745 @Override 746 public ValueSetExpansionOutcome expandVS(Resource src, ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heirarchical) throws FHIRException { 747 ValueSet vs = null; 748 vs = fetchResource(ValueSet.class, binding.getValueSet(), src); 749 if (vs == null) { 750 throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_VALUE_SET_, binding.getValueSet())); 751 } 752 return expandVS(vs, cacheOk, heirarchical); 753 } 754 755 756 @Override 757 public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean hierarchical, boolean noInactive) throws TerminologyServiceException { 758 ValueSet vs = new ValueSet(); 759 vs.setStatus(PublicationStatus.ACTIVE); 760 vs.setCompose(new ValueSetComposeComponent()); 761 vs.getCompose().setInactive(!noInactive); 762 vs.getCompose().getInclude().add(inc); 763 CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical); 764 ValueSetExpansionOutcome res; 765 res = txCache.getExpansion(cacheToken); 766 if (res != null) { 767 return res; 768 } 769 Parameters p = constructParameters(vs, hierarchical); 770 for (ConceptSetComponent incl : vs.getCompose().getInclude()) { 771 codeSystemsUsed.add(incl.getSystem()); 772 } 773 for (ConceptSetComponent incl : vs.getCompose().getExclude()) { 774 codeSystemsUsed.add(incl.getSystem()); 775 } 776 777 if (noTerminologyServer) { 778 return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE); 779 } 780 Map<String, String> params = new HashMap<String, String>(); 781 params.put("_limit", Integer.toString(expandCodesLimit )); 782 params.put("_incomplete", "true"); 783 txLog("$expand on "+txCache.summary(vs)); 784 try { 785 ValueSet result = tcc.getClient().expandValueset(vs, p, params); 786 res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId()); 787 } catch (Exception e) { 788 res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); 789 if (txLog != null) { 790 res.setTxLink(txLog.getLastId()); 791 } 792 } 793 txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT); 794 return res; 795 } 796 797 @Override 798 public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical) { 799 if (expParameters == null) 800 throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED)); 801 Parameters p = expParameters.copy(); 802 return expandVS(vs, cacheOk, heirarchical, false, p); 803 } 804 805 @Override 806 public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk) { 807 if (expParameters == null) 808 throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED)); 809 Parameters p = expParameters.copy(); 810 return expandVS(vs, cacheOk, heirarchical, incompleteOk, p); 811 } 812 813 public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean hierarchical, boolean incompleteOk, Parameters pIn) { 814 if (pIn == null) { 815 throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS)); 816 } 817 if (vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-time-units") || vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-distance-units")) { 818 return new ValueSetExpansionOutcome("This value set is not expanded correctly at this time (will be fixed in a future version)", TerminologyServiceErrorClass.VALUESET_UNSUPPORTED); 819 } 820 821 Parameters p = pIn.copy(); 822 823 if (vs.hasExpansion()) { 824 return new ValueSetExpansionOutcome(vs.copy()); 825 } 826 if (!vs.hasUrl()) { 827 throw new Error(formatMessage(I18nConstants.NO_VALUE_SET_IN_URL)); 828 } 829 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 830 codeSystemsUsed.add(inc.getSystem()); 831 } 832 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 833 codeSystemsUsed.add(inc.getSystem()); 834 } 835 836 CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical); 837 ValueSetExpansionOutcome res; 838 if (cacheOk) { 839 res = txCache.getExpansion(cacheToken); 840 if (res != null) { 841 return res; 842 } 843 } 844 845 p.setParameter("excludeNested", !hierarchical); 846 if (incompleteOk) { 847 p.setParameter("incomplete-ok", true); 848 } 849 850 List<String> allErrors = new ArrayList<>(); 851 852 // ok, first we try to expand locally 853 ValueSetExpander vse = constructValueSetExpanderSimple(); 854 res = null; 855 try { 856 res = vse.expand(vs, p); 857 } catch (Exception e) { 858 allErrors.addAll(vse.getAllErrors()); 859 e.printStackTrace(); 860 res = new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); 861 } 862 allErrors.addAll(vse.getAllErrors()); 863 if (res.getValueset() != null) { 864 if (!res.getValueset().hasUrl()) { 865 throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET)); 866 } 867 txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT); 868 return res; 869 } 870 if (res.getErrorClass() == TerminologyServiceErrorClass.INTERNAL_ERROR || isNoTerminologyServer()) { // this class is created specifically to say: don't consult the server 871 return new ValueSetExpansionOutcome(res.getError(), res.getErrorClass()); 872 } 873 874 // if that failed, we try to expand on the server 875 if (addDependentResources(p, vs)) { 876 p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId())); 877 } 878 879 if (noTerminologyServer) { 880 return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, allErrors); 881 } 882 Map<String, String> params = new HashMap<String, String>(); 883 params.put("_limit", Integer.toString(expandCodesLimit )); 884 params.put("_incomplete", "true"); 885 txLog("$expand on "+txCache.summary(vs)); 886 try { 887 ValueSet result = tcc.getClient().expandValueset(vs, p, params); 888 if (!result.hasUrl()) { 889 result.setUrl(vs.getUrl()); 890 } 891 if (!result.hasUrl()) { 892 throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2)); 893 } 894 res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId()); 895 } catch (Exception e) { 896 res = new ValueSetExpansionOutcome((e.getMessage() == null ? e.getClass().getName() : e.getMessage()), TerminologyServiceErrorClass.UNKNOWN, allErrors).setTxLink(txLog == null ? null : txLog.getLastId()); 897 } 898 txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT); 899 return res; 900 } 901 902 private boolean hasTooCostlyExpansion(ValueSet valueset) { 903 return valueset != null && valueset.hasExpansion() && ToolingExtensions.hasExtension(valueset.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY); 904 } 905 // --- validate code ------------------------------------------------------------------------------- 906 907 @Override 908 public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display) { 909 assert options != null; 910 Coding c = new Coding(system, version, code, display); 911 return validateCode(options, c, null); 912 } 913 914 @Override 915 public ValidationResult validateCode(ValidationOptions options, String system, String version, String code, String display, ValueSet vs) { 916 assert options != null; 917 Coding c = new Coding(system, version, code, display); 918 ValidationResult ret = validateCode(options, "$", c, vs); 919 ret.trimPath("$"); 920 return ret; 921 } 922 923 @Override 924 public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) { 925 assert options != null; 926 Coding c = new Coding(null, code, null); 927 return validateCode(options.withGuessSystem(), c, vs); 928 } 929 930 931 @Override 932 public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) { 933 if (options == null) { 934 options = ValidationOptions.defaults(); 935 } 936 // 1st pass: what is in the cache? 937 // 2nd pass: What can we do internally 938 // 3rd pass: hit the server 939 for (CodingValidationRequest t : codes) { 940 t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs, expParameters) : null); 941 if (t.getCoding().hasSystem()) { 942 codeSystemsUsed.add(t.getCoding().getSystem()); 943 } 944 if (txCache != null) { 945 t.setResult(txCache.getValidation(t.getCacheToken())); 946 } 947 } 948 if (options.isUseClient()) { 949 for (CodingValidationRequest t : codes) { 950 if (!t.hasResult()) { 951 try { 952 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs); 953 vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null); 954 ValidationResult res = vsc.validateCode("Coding", t.getCoding()); 955 if (txCache != null) { 956 txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT); 957 } 958 t.setResult(res); 959 } catch (Exception e) { 960 } 961 } 962 } 963 } 964 965 for (CodingValidationRequest t : codes) { 966 if (!t.hasResult()) { 967 String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem(); 968 if (!options.isUseServer()) { 969 t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null)); 970 } else if (unsupportedCodeSystems.contains(codeKey)) { 971 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null)); 972 } else if (noTerminologyServer) { 973 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, null)); 974 } 975 } 976 } 977 978 if (expParameters == null) 979 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 980 // for those that that failed, we try to validate on the server 981 Bundle batch = new Bundle(); 982 batch.setType(BundleType.BATCH); 983 Set<String> systems = new HashSet<>(); 984 for (CodingValidationRequest codingValidationRequest : codes) { 985 if (!codingValidationRequest.hasResult()) { 986 Parameters pIn = constructParameters(options, codingValidationRequest, vs); 987 setTerminologyOptions(options, pIn); 988 BundleEntryComponent be = batch.addEntry(); 989 be.setResource(pIn); 990 be.getRequest().setMethod(HTTPVerb.POST); 991 be.getRequest().setUrl("CodeSystem/$validate-code"); 992 be.setUserData("source", codingValidationRequest); 993 systems.add(codingValidationRequest.getCoding().getSystem()); 994 } 995 } 996 if (batch.getEntry().size() > 0) { 997 txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString()); 998 if (tcc.getClient() == null) { 999 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1000 } 1001 if (txLog != null) { 1002 txLog.clearLastId(); 1003 } 1004 Bundle resp = tcc.getClient().validateBatch(batch); 1005 if (resp == null) { 1006 throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE)); 1007 } 1008 for (int i = 0; i < batch.getEntry().size(); i++) { 1009 CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); 1010 BundleEntryComponent r = resp.getEntry().get(i); 1011 1012 if (r.getResource() instanceof Parameters) { 1013 t.setResult(processValidationResult((Parameters) r.getResource(), vs == null ? null : vs.getUrl())); 1014 if (txCache != null) { 1015 txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT); 1016 } 1017 } else { 1018 t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId())); 1019 } 1020 } 1021 } 1022 } 1023 1024 @Override 1025 public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl) { 1026 if (options == null) { 1027 options = ValidationOptions.defaults(); 1028 } 1029 // 1st pass: what is in the cache? 1030 // 2nd pass: What can we do internally 1031 // 3rd pass: hit the server 1032 for (CodingValidationRequest t : codes) { 1033 t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vsUrl, expParameters) : null); 1034 if (t.getCoding().hasSystem()) { 1035 codeSystemsUsed.add(t.getCoding().getSystem()); 1036 } 1037 if (txCache != null) { 1038 t.setResult(txCache.getValidation(t.getCacheToken())); 1039 } 1040 } 1041 if (options.isUseClient()) { 1042 ValueSet vs = fetchResource(ValueSet.class, vsUrl); 1043 if (vs != null) { 1044 for (CodingValidationRequest t : codes) { 1045 if (!t.hasResult()) { 1046 try { 1047 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs); 1048 vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null); 1049 ValidationResult res = vsc.validateCode("Coding", t.getCoding()); 1050 if (txCache != null) { 1051 txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT); 1052 } 1053 t.setResult(res); 1054 } catch (Exception e) { 1055 } 1056 } 1057 } 1058 } 1059 } 1060 1061 for (CodingValidationRequest t : codes) { 1062 if (!t.hasResult()) { 1063 String codeKey = t.getCoding().hasVersion() ? t.getCoding().getSystem()+"|"+t.getCoding().getVersion() : t.getCoding().getSystem(); 1064 if (!options.isUseServer()) { 1065 t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null)); 1066 } else if (unsupportedCodeSystems.contains(codeKey)) { 1067 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null)); 1068 } else if (noTerminologyServer) { 1069 t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, null)); 1070 } 1071 } 1072 } 1073 1074 if (expParameters == null) 1075 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1076 // for those that that failed, we try to validate on the server 1077 Bundle batch = new Bundle(); 1078 batch.setType(BundleType.BATCH); 1079 Set<String> systems = new HashSet<>(); 1080 for (CodingValidationRequest codingValidationRequest : codes) { 1081 if (!codingValidationRequest.hasResult()) { 1082 Parameters pIn = constructParameters(options, codingValidationRequest, vsUrl); 1083 setTerminologyOptions(options, pIn); 1084 BundleEntryComponent be = batch.addEntry(); 1085 be.setResource(pIn); 1086 be.getRequest().setMethod(HTTPVerb.POST); 1087 if (vsUrl != null) { 1088 be.getRequest().setUrl("ValueSet/$validate-code"); 1089 } else { 1090 be.getRequest().setUrl("CodeSystem/$validate-code"); 1091 } 1092 be.setUserData("source", codingValidationRequest); 1093 systems.add(codingValidationRequest.getCoding().getSystem()); 1094 } 1095 } 1096 if (batch.getEntry().size() > 0) { 1097 txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString()); 1098 if (tcc.getClient() == null) { 1099 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1100 } 1101 if (txLog != null) { 1102 txLog.clearLastId(); 1103 } 1104 Bundle resp = tcc.getClient().validateBatch(batch); 1105 if (resp == null) { 1106 throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE)); 1107 } 1108 for (int i = 0; i < batch.getEntry().size(); i++) { 1109 CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); 1110 BundleEntryComponent r = resp.getEntry().get(i); 1111 1112 if (r.getResource() instanceof Parameters) { 1113 t.setResult(processValidationResult((Parameters) r.getResource(), vsUrl)); 1114 if (txCache != null) { 1115 txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT); 1116 } 1117 } else { 1118 t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource()), null).setTxLink(txLog == null ? null : txLog.getLastId())); 1119 } 1120 } 1121 } 1122 } 1123 1124 private String getResponseText(Resource resource) { 1125 if (resource instanceof OperationOutcome) { 1126 return OperationOutcomeRenderer.toString((OperationOutcome) resource); 1127 } 1128 return "Todo"; 1129 } 1130 1131 @Override 1132 public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) { 1133 ValidationContextCarrier ctxt = new ValidationContextCarrier(); 1134 return validateCode(options, "Coding", code, vs, ctxt); 1135 } 1136 1137 public ValidationResult validateCode(ValidationOptions options, String path, Coding code, ValueSet vs) { 1138 ValidationContextCarrier ctxt = new ValidationContextCarrier(); 1139 return validateCode(options, path, code, vs, ctxt); 1140 } 1141 1142 private final String getCodeKey(Coding code) { 1143 return code.hasVersion() ? code.getSystem()+"|"+code.getVersion() : code.getSystem(); 1144 } 1145 1146 @Override 1147 public ValidationResult validateCode(final ValidationOptions optionsArg, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) { 1148 return validateCode(optionsArg, "Coding", code, vs, ctxt); 1149 } 1150 1151 public ValidationResult validateCode(final ValidationOptions optionsArg, String path, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) { 1152 1153 ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults(); 1154 1155 if (code.hasSystem()) { 1156 codeSystemsUsed.add(code.getSystem()); 1157 } 1158 1159 final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateValidationToken(options, code, vs, expParameters) : null; 1160 ValidationResult res = null; 1161 if (cachingAllowed && txCache != null) { 1162 res = txCache.getValidation(cacheToken); 1163 } 1164 if (res != null) { 1165 updateUnsupportedCodeSystems(res, code, getCodeKey(code)); 1166 return res; 1167 } 1168 1169 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1170 Set<String> unknownSystems = new HashSet<>(); 1171 1172 String localError = null; 1173 String localWarning = null; 1174 if (options.isUseClient()) { 1175 // ok, first we try to validate locally 1176 try { 1177 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt); 1178 vsc.setUnknownSystems(unknownSystems); 1179 vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null); 1180 if (!ValueSetUtilities.isServerSide(code.getSystem())) { 1181 res = vsc.validateCode(path, code); 1182 if (txCache != null && cachingAllowed) { 1183 txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); 1184 } 1185 return res; 1186 } 1187 } catch (VSCheckerException e) { 1188 if (e.isWarning()) { 1189 localWarning = e.getMessage(); 1190 } else { 1191 localError = e.getMessage(); 1192 } 1193 if (e.getIssues() != null) { 1194 issues.addAll(e.getIssues()); 1195 } 1196 } catch (TerminologyServiceProtectionException e) { 1197 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType()); 1198 iss.getDetails().setText(e.getMessage()); 1199 issues.add(iss); 1200 return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues); 1201 } catch (Exception e) { 1202// e.printStackTrace(); 1203 localError = e.getMessage(); 1204 } 1205 } 1206 1207 if (localError != null && tcc.getClient() == null) { 1208 if (unknownSystems.size() > 0) { 1209 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems); 1210 } else { 1211 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues); 1212 } 1213 } 1214 if (localWarning != null && tcc.getClient() == null) { 1215 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1216 } 1217 if (!options.isUseServer()) { 1218 if (localWarning != null) { 1219 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1220 } else { 1221 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localError), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1222 } 1223 } 1224 String codeKey = getCodeKey(code); 1225 if (unsupportedCodeSystems.contains(codeKey)) { 1226 return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, code.getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues); 1227 } 1228 1229 // if that failed, we try to validate on the server 1230 if (noTerminologyServer) { 1231 return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, issues); 1232 } 1233 String csumm =cachingAllowed && txCache != null ? txCache.summary(code) : null; 1234 if (cachingAllowed && txCache != null) { 1235 txLog("$validate "+csumm+" for "+ txCache.summary(vs)); 1236 } else { 1237 txLog("$validate "+csumm+" before cache exists"); 1238 } 1239 try { 1240 Parameters pIn = constructParameters(options, code); 1241 res = validateOnServer(vs, pIn, options); 1242 } catch (Exception e) { 1243 res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR); 1244 } 1245 if (!res.isOk() && localError != null) { 1246 res.setDiagnostics("Local Error: "+localError.trim()+". Server Error: "+res.getMessage()); 1247 } 1248 updateUnsupportedCodeSystems(res, code, codeKey); 1249 if (cachingAllowed && txCache != null) { // we never cache unsupported code systems - we always keep trying (but only once per run) 1250 txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT); 1251 } 1252 return res; 1253 } 1254 1255 protected ValueSetExpander constructValueSetExpanderSimple() { 1256 return new ValueSetExpander(this, new TerminologyOperationContext(this)); 1257 } 1258 1259 protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) { 1260 return new ValueSetValidator(this, new TerminologyOperationContext(this), options, vs, ctxt, expParameters, tcc.getTxcaps()); 1261 } 1262 1263 protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) { 1264 return new ValueSetValidator(this, new TerminologyOperationContext(this), options, vs, expParameters, tcc.getTxcaps()); 1265 } 1266 1267 protected Parameters constructParameters(ValueSet vs, boolean hierarchical) { 1268 Parameters p = expParameters.copy(); 1269 p.setParameter("includeDefinition", false); 1270 p.setParameter("excludeNested", !hierarchical); 1271 1272 boolean cached = addDependentResources(p, vs); 1273 if (cached) { 1274 p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId())); 1275 } 1276 return p; 1277 } 1278 1279 protected Parameters constructParameters(ValidationOptions options, Coding coding) { 1280 Parameters pIn = new Parameters(); 1281 pIn.addParameter().setName("coding").setValue(coding); 1282 if (options.isGuessSystem()) { 1283 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1284 } 1285 setTerminologyOptions(options, pIn); 1286 return pIn; 1287 } 1288 1289 protected Parameters constructParameters(ValidationOptions options, CodeableConcept codeableConcept) { 1290 Parameters pIn = new Parameters(); 1291 pIn.addParameter().setName("codeableConcept").setValue(codeableConcept); 1292 setTerminologyOptions(options, pIn); 1293 return pIn; 1294 } 1295 1296 protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, ValueSet valueSet) { 1297 Parameters pIn = new Parameters(); 1298 pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding()); 1299 if (options.isGuessSystem()) { 1300 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1301 } 1302 if (valueSet != null) { 1303 pIn.addParameter().setName("valueSet").setResource(valueSet); 1304 } 1305 pIn.addParameter().setName("profile").setResource(expParameters); 1306 return pIn; 1307 } 1308 1309 protected Parameters constructParameters(ValidationOptions options, CodingValidationRequest codingValidationRequest, String vsUrl) { 1310 Parameters pIn = new Parameters(); 1311 pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding()); 1312 if (options.isGuessSystem()) { 1313 pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true)); 1314 } 1315 if (vsUrl != null) { 1316 pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl)); 1317 } 1318 pIn.addParameter().setName("profile").setResource(expParameters); 1319 return pIn; 1320 } 1321 1322 private void updateUnsupportedCodeSystems(ValidationResult res, Coding code, String codeKey) { 1323 if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED && !code.hasVersion()) { 1324 unsupportedCodeSystems.add(codeKey); 1325 } 1326 } 1327 1328 private void setTerminologyOptions(ValidationOptions options, Parameters pIn) { 1329 if (options.hasLanguages()) { 1330 pIn.addParameter("displayLanguage", options.getLanguages().toString()); 1331 } 1332 if (options.getValueSetMode() != ValueSetMode.ALL_CHECKS) { 1333 pIn.addParameter("valueSetMode", options.getValueSetMode().toString()); 1334 } 1335 if (options.isVersionFlexible()) { 1336 pIn.addParameter("default-to-latest-version", true); 1337 } 1338 } 1339 1340 @Override 1341 public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) { 1342 CacheToken cacheToken = txCache.generateValidationToken(options, code, vs, expParameters); 1343 ValidationResult res = null; 1344 if (cachingAllowed) { 1345 res = txCache.getValidation(cacheToken); 1346 if (res != null) { 1347 return res; 1348 } 1349 } 1350 for (Coding c : code.getCoding()) { 1351 if (c.hasSystem()) { 1352 codeSystemsUsed.add(c.getSystem()); 1353 } 1354 } 1355 Set<String> unknownSystems = new HashSet<>(); 1356 1357 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1358 1359 String localError = null; 1360 String localWarning = null; 1361 1362 if (options.isUseClient()) { 1363 // ok, first we try to validate locally 1364 try { 1365 ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs); 1366 vsc.setUnknownSystems(unknownSystems); 1367 vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null); 1368 res = vsc.validateCode("CodeableConcept", code); 1369 if (cachingAllowed) { 1370 txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); 1371 } 1372 return res; 1373 } catch (VSCheckerException e) { 1374 if (e.isWarning()) { 1375 localWarning = e.getMessage(); 1376 } else { 1377 localError = e.getMessage(); 1378 } 1379 if (e.getIssues() != null) { 1380 issues.addAll(e.getIssues()); 1381 } 1382 } catch (TerminologyServiceProtectionException e) { 1383 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType()); 1384 iss.getDetails().setText(e.getMessage()); 1385 issues.add(iss); 1386 return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues); 1387 } catch (Exception e) { 1388// e.printStackTrace(); 1389 localError = e.getMessage(); 1390 } 1391 } 1392 1393 if (localError != null && tcc.getClient() == null) { 1394 if (unknownSystems.size() > 0) { 1395 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems); 1396 } else { 1397 return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues); 1398 } 1399 } 1400 if (localWarning != null && tcc.getClient() == null) { 1401 return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues); 1402 } 1403 1404 if (!options.isUseServer()) { 1405 return new ValidationResult(IssueSeverity.WARNING, "Unable to validate code without using server", TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null); 1406 } 1407 1408 // if that failed, we try to validate on the server 1409 if (noTerminologyServer) { 1410 return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services", TerminologyServiceErrorClass.NOSERVICE, null); 1411 } 1412 txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs)); 1413 try { 1414 Parameters pIn = constructParameters(options, code); 1415 res = validateOnServer(vs, pIn, options); 1416 } catch (Exception e) { 1417 res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()); 1418 } 1419 if (cachingAllowed) { 1420 txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT); 1421 } 1422 return res; 1423 } 1424 1425 protected ValidationResult validateOnServer(ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException { 1426 1427 if (vs != null) { 1428 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1429 codeSystemsUsed.add(inc.getSystem()); 1430 } 1431 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1432 codeSystemsUsed.add(inc.getSystem()); 1433 } 1434 } 1435 1436 addServerValidationParameters(vs, pin, options); 1437 1438 if (txLog != null) { 1439 txLog.clearLastId(); 1440 } 1441 if (tcc.getClient() == null) { 1442 throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); 1443 } 1444 Parameters pOut; 1445 if (vs == null) { 1446 pOut = tcc.getClient().validateCS(pin); 1447 } else { 1448 pOut = tcc.getClient().validateVS(pin); 1449 } 1450 return processValidationResult(pOut, vs == null ? null : vs.getUrl()); 1451 } 1452 1453 protected void addServerValidationParameters(ValueSet vs, Parameters pin, ValidationOptions options) { 1454 boolean cache = false; 1455 if (vs != null) { 1456 if (tcc.isTxCaching() && tcc.getCacheId() != null && vs.getUrl() != null && tcc.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) { 1457 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+ vs.getVersion() : ""))); 1458 } else if (options.getVsAsUrl()){ 1459 pin.addParameter().setName("url").setValue(new UriType(vs.getUrl())); 1460 } else { 1461 pin.addParameter().setName("valueSet").setResource(vs); 1462 if (vs.getUrl() != null) { 1463 tcc.getCached().add(vs.getUrl()+"|"+ vs.getVersion()); 1464 } 1465 } 1466 cache = true; 1467 addDependentResources(pin, vs); 1468 } 1469 if (cache) { 1470 pin.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId())); 1471 } 1472 for (ParametersParameterComponent pp : pin.getParameter()) { 1473 if (pp.getName().equals("profile")) { 1474 throw new Error(formatMessage(I18nConstants.CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT)); 1475 } 1476 } 1477 if (expParameters == null) { 1478 throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); 1479 } 1480 pin.addParameter().setName("profile").setResource(expParameters); 1481 1482 if (options.isDisplayWarningMode()) { 1483 pin.addParameter("mode","lenient-display-validation"); 1484 } 1485 } 1486 1487 private boolean addDependentResources(Parameters pin, ValueSet vs) { 1488 boolean cache = false; 1489 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1490 cache = addDependentResources(pin, inc, vs) || cache; 1491 } 1492 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1493 cache = addDependentResources(pin, inc, vs) || cache; 1494 } 1495 return cache; 1496 } 1497 1498 private boolean addDependentResources(Parameters pin, ConceptSetComponent inc, Resource src) { 1499 boolean cache = false; 1500 for (CanonicalType c : inc.getValueSet()) { 1501 ValueSet vs = fetchResource(ValueSet.class, c.getValue(), src); 1502 if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) { 1503 cache = checkAddToParams(pin, vs) || cache; 1504 addDependentResources(pin, vs); 1505 } 1506 } 1507 CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src); 1508 if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) { 1509 cache = checkAddToParams(pin, cs) || cache; 1510 // todo: supplements 1511 } 1512 return cache; 1513 } 1514 1515 private boolean checkAddToParams(Parameters pin, CanonicalResource cr) { 1516 boolean cache = false; 1517 boolean addToParams = false; 1518 if (tcc.usingCache()) { 1519 if (!tcc.alreadyCached(cr)) { 1520 tcc.addToCache(cr); 1521 System.out.println("add to cache: "+cr.getVUrl()); 1522 addToParams = true; 1523 cache = true; 1524 } else { 1525 System.out.println("already cached: "+cr.getVUrl()); 1526 } 1527 } else { 1528 addToParams = true; 1529 } 1530 if (addToParams) { 1531 pin.addParameter().setName("tx-resource").setResource(cr); 1532 } 1533 return cache; 1534 } 1535 1536 private boolean hasCanonicalResource(Parameters pin, String name, String vUrl) { 1537 for (ParametersParameterComponent p : pin.getParameter()) { 1538 if (name.equals(p.getName()) && p.hasResource() && 1539 p.getResource() instanceof CanonicalResource && vUrl.equals(((CanonicalResource) p.getResource()).getVUrl())) { 1540 return true; 1541 } 1542 } 1543 return false; 1544 } 1545 1546 public ValidationResult processValidationResult(Parameters pOut, String vs) { 1547 boolean ok = false; 1548 String message = "No Message returned"; 1549 String display = null; 1550 String system = null; 1551 String code = null; 1552 String version = null; 1553 boolean inactive = false; 1554 String status = null; 1555 List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); 1556 Set<String> unknownSystems = new HashSet<>(); 1557 1558 TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN; 1559 for (ParametersParameterComponent p : pOut.getParameter()) { 1560 if (p.hasValue()) { 1561 if (p.getName().equals("result")) { 1562 ok = ((BooleanType) p.getValue()).getValue().booleanValue(); 1563 } else if (p.getName().equals("message")) { 1564 message = p.getValue().primitiveValue(); 1565 } else if (p.getName().equals("display")) { 1566 display = p.getValue().primitiveValue(); 1567 } else if (p.getName().equals("system")) { 1568 system = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1569 } else if (p.getName().equals("version")) { 1570 version = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1571 } else if (p.getName().equals("code")) { 1572 code = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1573 } else if (p.getName().equals("inactive")) { 1574 inactive = "true".equals(((PrimitiveType<?>) p.getValue()).asStringValue()); 1575 } else if (p.getName().equals("status")) { 1576 status = ((PrimitiveType<?>) p.getValue()).asStringValue(); 1577 } else if (p.getName().equals("x-caused-by-unknown-system")) { 1578 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 1579 unknownSystems.add(((PrimitiveType<?>) p.getValue()).asStringValue()); 1580 } else if (p.getName().equals("warning-withdrawn")) { 1581 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1582 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_WITHDRAWN : I18nConstants.MSG_WITHDRAWN_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs)); 1583 issues.add(iss); 1584 } else if (p.getName().equals("warning-deprecated")) { 1585 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1586 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DEPRECATED : I18nConstants.MSG_DEPRECATED_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs)); 1587 issues.add(iss); 1588 } else if (p.getName().equals("warning-retired")) { 1589 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1590 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_RETIRED : I18nConstants.MSG_RETIRED_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs)); 1591 issues.add(iss); 1592 } else if (p.getName().equals("warning-experimental")) { 1593 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1594 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_EXPERIMENTAL : I18nConstants.MSG_EXPERIMENTAL_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs)); 1595 issues.add(iss); 1596 } else if (p.getName().equals("warning-draft")) { 1597 OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); 1598 iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DRAFT : I18nConstants.MSG_DRAFT_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs)); 1599 issues.add(iss); 1600 } else if (p.getName().equals("cause")) { 1601 try { 1602 IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue()); 1603 if (it == IssueType.UNKNOWN) { 1604 err = TerminologyServiceErrorClass.UNKNOWN; 1605 } else if (it == IssueType.NOTFOUND) { 1606 err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED; 1607 } else if (it == IssueType.NOTSUPPORTED) { 1608 err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED; 1609 } else { 1610 err = null; 1611 } 1612 } catch (FHIRException e) { 1613 } 1614 } 1615 } 1616 } 1617 ValidationResult res = null; 1618 if (!ok) { 1619 res = new ValidationResult(IssueSeverity.ERROR, message+" (from "+tcc.getClient().getId()+")", err, null).setTxLink(txLog.getLastId()); 1620 } else if (message != null && !message.equals("No Message returned")) { 1621 res = new ValidationResult(IssueSeverity.WARNING, message+" (from "+tcc.getClient().getId()+")", system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog.getLastId()); 1622 } else if (display != null) { 1623 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display).setTxLink(txLog.getLastId()); 1624 } else { 1625 res = new ValidationResult(system, version, new ConceptDefinitionComponent().setCode(code), null).setTxLink(txLog.getLastId()); 1626 } 1627 res.setIssues(issues); 1628 res.setStatus(inactive, status); 1629 res.setUnknownSystems(unknownSystems); 1630 return res; 1631 } 1632 1633 // -------------------------------------------------------------------------------------------------------------------------------------------------------- 1634 1635 protected void initTS(String cachePath) throws IOException { 1636 if (cachePath != null && !new File(cachePath).exists()) { 1637 Utilities.createDirectory(cachePath); 1638 } 1639 txCache = new TerminologyCache(lock, cachePath); 1640 } 1641 1642 public void clearTSCache(String url) throws Exception { 1643 txCache.removeCS(url); 1644 } 1645 1646 public void clearTS() { 1647 txCache.clear(); 1648 } 1649 1650 public boolean isCanRunWithoutTerminology() { 1651 return canRunWithoutTerminology; 1652 } 1653 1654 public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) { 1655 this.canRunWithoutTerminology = canRunWithoutTerminology; 1656 } 1657 1658 public void setLogger(@Nonnull ILoggingService logger) { 1659 this.logger = logger; 1660 } 1661 1662 public Parameters getExpansionParameters() { 1663 return expParameters; 1664 } 1665 1666 public void setExpansionProfile(Parameters expParameters) { 1667 this.expParameters = expParameters; 1668 } 1669 1670 @Override 1671 public boolean isNoTerminologyServer() { 1672 return noTerminologyServer || tcc.getClient() == null; 1673 } 1674 1675 public void setNoTerminologyServer(boolean noTerminologyServer) { 1676 this.noTerminologyServer = noTerminologyServer; 1677 } 1678 1679 public String getName() { 1680 return name; 1681 } 1682 1683 public void setName(String name) { 1684 this.name = name; 1685 } 1686 1687 @Override 1688 public Set<String> getResourceNamesAsSet() { 1689 Set<String> res = new HashSet<String>(); 1690 res.addAll(getResourceNames()); 1691 return res; 1692 } 1693 1694 public boolean isAllowLoadingDuplicates() { 1695 return allowLoadingDuplicates; 1696 } 1697 1698 public void setAllowLoadingDuplicates(boolean allowLoadingDuplicates) { 1699 this.allowLoadingDuplicates = allowLoadingDuplicates; 1700 } 1701 1702 @Override 1703 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException { 1704 return fetchResourceWithException(class_, uri, null); 1705 } 1706 1707 public <T extends Resource> T fetchResourceWithException(String cls, String uri) throws FHIRException { 1708 return fetchResourceWithExceptionByVersion(cls, uri, null, null); 1709 } 1710 1711 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, Resource sourceForReference) throws FHIRException { 1712 return fetchResourceWithExceptionByVersion(class_, uri, null, sourceForReference); 1713 } 1714 1715 @SuppressWarnings("unchecked") 1716 public <T extends Resource> T fetchResourceWithExceptionByVersion(Class<T> class_, String uri, String version, Resource sourceForReference) throws FHIRException { 1717 if (uri == null) { 1718 return null; 1719 } 1720 1721 if (QA_CHECK_REFERENCE_SOURCE) { 1722 // it can be tricky to trace the source of a reference correctly. The code isn't water tight, 1723 // particularly around snapshot generation. Enable this code to check that the references are 1724 // correct (but it's slow) 1725 if (sourceForReference != null && uri.contains("ValueSet")) { 1726 if (!ResourceUtilities.hasURL(uri, sourceForReference)) { 1727 System.out.print("Claimed source doesn't have url in it: "+sourceForReference.fhirType()+"/"+sourceForReference.getIdPart()+" -> "+uri); 1728 System.out.println(); 1729 } 1730 } 1731 } 1732 1733 List<String> pvlist = new ArrayList<>(); 1734 if (sourceForReference != null && sourceForReference.getSourcePackage() != null) { 1735 populatePVList(pvlist, sourceForReference.getSourcePackage()); 1736 } 1737 1738 if (class_ == StructureDefinition.class) { 1739 uri = ProfileUtilities.sdNs(uri, null); 1740 } 1741 synchronized (lock) { 1742 1743 if (version == null) { 1744 if (uri.contains("|")) { 1745 version = uri.substring(uri.lastIndexOf("|")+1); 1746 uri = uri.substring(0, uri.lastIndexOf("|")); 1747 } 1748 } else { 1749 assert !uri.contains("|"); 1750 } 1751 if (uri.contains("#")) { 1752 uri = uri.substring(0, uri.indexOf("#")); 1753 } 1754 if (class_ == Resource.class || class_ == null) { 1755 if (structures.has(uri)) { 1756 return (T) structures.get(uri, version, pvlist); 1757 } 1758 if (guides.has(uri)) { 1759 return (T) guides.get(uri, version, pvlist); 1760 } 1761 if (capstmts.has(uri)) { 1762 return (T) capstmts.get(uri, version, pvlist); 1763 } 1764 if (measures.has(uri)) { 1765 return (T) measures.get(uri, version, pvlist); 1766 } 1767 if (libraries.has(uri)) { 1768 return (T) libraries.get(uri, version, pvlist); 1769 } 1770 if (valueSets.has(uri)) { 1771 return (T) valueSets.get(uri, version, pvlist); 1772 } 1773 if (codeSystems.has(uri)) { 1774 return (T) codeSystems.get(uri, version, pvlist); 1775 } 1776 if (operations.has(uri)) { 1777 return (T) operations.get(uri, version, pvlist); 1778 } 1779 if (searchParameters.has(uri)) { 1780 return (T) searchParameters.get(uri, version, pvlist); 1781 } 1782 if (plans.has(uri)) { 1783 return (T) plans.get(uri, version, pvlist); 1784 } 1785 if (maps.has(uri)) { 1786 return (T) maps.get(uri, version, pvlist); 1787 } 1788 if (transforms.has(uri)) { 1789 return (T) transforms.get(uri, version, pvlist); 1790 } 1791 if (actors.has(uri)) { 1792 return (T) transforms.get(uri, version, pvlist); 1793 } 1794 if (requirements.has(uri)) { 1795 return (T) transforms.get(uri, version, pvlist); 1796 } 1797 if (questionnaires.has(uri)) { 1798 return (T) questionnaires.get(uri, version, pvlist); 1799 } 1800 1801 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 1802 for (ResourceProxy r : rt.values()) { 1803 if (uri.equals(r.getUrl())) { 1804 if (version == null || version == r.getResource().getMeta().getVersionId()) { 1805 return (T) r.getResource(); 1806 } 1807 } 1808 } 1809 } 1810 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 1811 return null; 1812 } 1813 1814 // it might be a special URL. 1815// if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 1816// Resource res = null; // findTxValueSet(uri); 1817// if (res != null) { 1818// return (T) res; 1819// } 1820// } 1821 return null; 1822 } else if (class_ == ImplementationGuide.class) { 1823 return (T) guides.get(uri, version, pvlist); 1824 } else if (class_ == CapabilityStatement.class) { 1825 return (T) capstmts.get(uri, version, pvlist); 1826 } else if (class_ == Measure.class) { 1827 return (T) measures.get(uri, version, pvlist); 1828 } else if (class_ == Library.class) { 1829 return (T) libraries.get(uri, version, pvlist); 1830 } else if (class_ == StructureDefinition.class) { 1831 return (T) structures.get(uri, version, pvlist); 1832 } else if (class_ == StructureMap.class) { 1833 return (T) transforms.get(uri, version, pvlist); 1834 } else if (class_ == ValueSet.class) { 1835 return (T) valueSets.get(uri, version, pvlist); 1836 } else if (class_ == CodeSystem.class) { 1837 return (T) codeSystems.get(uri, version, pvlist); 1838 } else if (class_ == ConceptMap.class) { 1839 return (T) maps.get(uri, version, pvlist); 1840 } else if (class_ == ActorDefinition.class) { 1841 return (T) actors.get(uri, version, pvlist); 1842 } else if (class_ == Requirements.class) { 1843 return (T) requirements.get(uri, version, pvlist); 1844 } else if (class_ == PlanDefinition.class) { 1845 return (T) plans.get(uri, version, pvlist); 1846 } else if (class_ == OperationDefinition.class) { 1847 OperationDefinition od = operations.get(uri, version); 1848 return (T) od; 1849 } else if (class_ == Questionnaire.class) { 1850 return (T) questionnaires.get(uri, version, pvlist); 1851 } else if (class_ == SearchParameter.class) { 1852 SearchParameter res = searchParameters.get(uri, version, pvlist); 1853 return (T) res; 1854 } 1855 if (class_ == CodeSystem.class && codeSystems.has(uri)) { 1856 return (T) codeSystems.get(uri, version, pvlist); 1857 } 1858 if (class_ == ValueSet.class && valueSets.has(uri)) { 1859 return (T) valueSets.get(uri, version, pvlist); 1860 } 1861 1862 if (class_ == Questionnaire.class) { 1863 return (T) questionnaires.get(uri, version, pvlist); 1864 } 1865 if (supportedCodeSystems.contains(uri)) { 1866 return null; 1867 } 1868 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 1869 } 1870 } 1871 1872 private void populatePVList(List<String> pvlist, PackageInformation sourcePackage) { 1873 pvlist.add(sourcePackage.getVID()); 1874 List<String> toadd = new ArrayList<>(); 1875 do { 1876 toadd.clear(); 1877 for (String s : pvlist) { 1878 PackageInformation pi = packages.get(s); 1879 if (pi != null) { 1880 for (String v : pi.getDependencies()) { 1881 if (!pvlist.contains(v) && !toadd.contains(v)) { 1882 toadd.add(v); 1883 } 1884 } 1885 } 1886 } 1887 pvlist.addAll(toadd); 1888 } while (toadd.size() > 0); 1889 } 1890 1891 public PackageInformation getPackageForUrl(String uri) { 1892 if (uri == null) { 1893 return null; 1894 } 1895 uri = ProfileUtilities.sdNs(uri, null); 1896 1897 synchronized (lock) { 1898 1899 String version = null; 1900 if (uri.contains("|")) { 1901 version = uri.substring(uri.lastIndexOf("|")+1); 1902 uri = uri.substring(0, uri.lastIndexOf("|")); 1903 } 1904 if (uri.contains("#")) { 1905 uri = uri.substring(0, uri.indexOf("#")); 1906 } 1907 if (structures.has(uri)) { 1908 return structures.getPackageInfo(uri, version); 1909 } 1910 if (guides.has(uri)) { 1911 return guides.getPackageInfo(uri, version); 1912 } 1913 if (capstmts.has(uri)) { 1914 return capstmts.getPackageInfo(uri, version); 1915 } 1916 if (measures.has(uri)) { 1917 return measures.getPackageInfo(uri, version); 1918 } 1919 if (libraries.has(uri)) { 1920 return libraries.getPackageInfo(uri, version); 1921 } 1922 if (valueSets.has(uri)) { 1923 return valueSets.getPackageInfo(uri, version); 1924 } 1925 if (codeSystems.has(uri)) { 1926 return codeSystems.getPackageInfo(uri, version); 1927 } 1928 if (operations.has(uri)) { 1929 return operations.getPackageInfo(uri, version); 1930 } 1931 if (searchParameters.has(uri)) { 1932 return searchParameters.getPackageInfo(uri, version); 1933 } 1934 if (plans.has(uri)) { 1935 return plans.getPackageInfo(uri, version); 1936 } 1937 if (maps.has(uri)) { 1938 return maps.getPackageInfo(uri, version); 1939 } 1940 if (transforms.has(uri)) { 1941 return transforms.getPackageInfo(uri, version); 1942 } 1943 if (actors.has(uri)) { 1944 return actors.getPackageInfo(uri, version); 1945 } 1946 if (requirements.has(uri)) { 1947 return requirements.getPackageInfo(uri, version); 1948 } 1949 if (questionnaires.has(uri)) { 1950 return questionnaires.getPackageInfo(uri, version); 1951 } 1952 return null; 1953 } 1954 } 1955 1956 @SuppressWarnings("unchecked") 1957 public <T extends Resource> T fetchResourceWithExceptionByVersion(String cls, String uri, String version, CanonicalResource source) throws FHIRException { 1958 if (uri == null) { 1959 return null; 1960 } 1961 1962 if ("StructureDefinition".equals(cls)) { 1963 uri = ProfileUtilities.sdNs(uri, null); 1964 } 1965 synchronized (lock) { 1966 1967 if (version == null) { 1968 if (uri.contains("|")) { 1969 version = uri.substring(uri.lastIndexOf("|")+1); 1970 uri = uri.substring(0, uri.lastIndexOf("|")); 1971 } 1972 } else { 1973 boolean b = !uri.contains("|"); 1974 assert b; 1975 } 1976 if (uri.contains("#")) { 1977 uri = uri.substring(0, uri.indexOf("#")); 1978 } 1979 if (cls == null || "Resource".equals(cls)) { 1980 if (structures.has(uri)) { 1981 return (T) structures.get(uri, version); 1982 } 1983 if (guides.has(uri)) { 1984 return (T) guides.get(uri, version); 1985 } 1986 if (capstmts.has(uri)) { 1987 return (T) capstmts.get(uri, version); 1988 } 1989 if (measures.has(uri)) { 1990 return (T) measures.get(uri, version); 1991 } 1992 if (libraries.has(uri)) { 1993 return (T) libraries.get(uri, version); 1994 } 1995 if (valueSets.has(uri)) { 1996 return (T) valueSets.get(uri, version); 1997 } 1998 if (codeSystems.has(uri)) { 1999 return (T) codeSystems.get(uri, version); 2000 } 2001 if (operations.has(uri)) { 2002 return (T) operations.get(uri, version); 2003 } 2004 if (searchParameters.has(uri)) { 2005 return (T) searchParameters.get(uri, version); 2006 } 2007 if (plans.has(uri)) { 2008 return (T) plans.get(uri, version); 2009 } 2010 if (maps.has(uri)) { 2011 return (T) maps.get(uri, version); 2012 } 2013 if (transforms.has(uri)) { 2014 return (T) transforms.get(uri, version); 2015 } 2016 if (actors.has(uri)) { 2017 return (T) actors.get(uri, version); 2018 } 2019 if (requirements.has(uri)) { 2020 return (T) requirements.get(uri, version); 2021 } 2022 if (questionnaires.has(uri)) { 2023 return (T) questionnaires.get(uri, version); 2024 } 2025 for (Map<String, ResourceProxy> rt : allResourcesById.values()) { 2026 for (ResourceProxy r : rt.values()) { 2027 if (uri.equals(r.getUrl())) { 2028 return (T) r.getResource(); 2029 } 2030 } 2031 } 2032 } else if ("ImplementationGuide".equals(cls)) { 2033 return (T) guides.get(uri, version); 2034 } else if ("CapabilityStatement".equals(cls)) { 2035 return (T) capstmts.get(uri, version); 2036 } else if ("Measure".equals(cls)) { 2037 return (T) measures.get(uri, version); 2038 } else if ("Library".equals(cls)) { 2039 return (T) libraries.get(uri, version); 2040 } else if ("StructureDefinition".equals(cls)) { 2041 return (T) structures.get(uri, version); 2042 } else if ("StructureMap".equals(cls)) { 2043 return (T) transforms.get(uri, version); 2044 } else if ("Requirements".equals(cls)) { 2045 return (T) requirements.get(uri, version); 2046 } else if ("ActorDefinition".equals(cls)) { 2047 return (T) actors.get(uri, version); 2048 } else if ("ValueSet".equals(cls)) { 2049 return (T) valueSets.get(uri, version); 2050 } else if ("CodeSystem".equals(cls)) { 2051 return (T) codeSystems.get(uri, version); 2052 } else if ("ConceptMap".equals(cls)) { 2053 return (T) maps.get(uri, version); 2054 } else if ("PlanDefinition".equals(cls)) { 2055 return (T) plans.get(uri, version); 2056 } else if ("OperationDefinition".equals(cls)) { 2057 OperationDefinition od = operations.get(uri, version); 2058 return (T) od; 2059 } else if ("Questionnaire.class".equals(cls)) { 2060 return (T) questionnaires.get(uri, version); 2061 } else if ("SearchParameter.class".equals(cls)) { 2062 SearchParameter res = searchParameters.get(uri, version); 2063 return (T) res; 2064 } 2065 if ("CodeSystem".equals(cls) && codeSystems.has(uri)) { 2066 return (T) codeSystems.get(uri, version); 2067 } 2068 if ("ValueSet".equals(cls) && valueSets.has(uri)) { 2069 return (T) valueSets.get(uri, version); 2070 } 2071 2072 if ("Questionnaire".equals(cls)) { 2073 return (T) questionnaires.get(uri, version); 2074 } 2075 if (cls == null) { 2076 if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) { 2077 return null; 2078 } 2079 2080 // it might be a special URL. 2081 if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) { 2082 Resource res = null; // findTxValueSet(uri); 2083 if (res != null) { 2084 return (T) res; 2085 } 2086 } 2087 return null; 2088 } 2089 if (supportedCodeSystems.contains(uri)) { 2090 return null; 2091 } 2092 throw new FHIRException(formatMessage(I18nConstants.NOT_DONE_YET_CANT_FETCH_, uri)); 2093 } 2094 } 2095 2096 @SuppressWarnings("unchecked") 2097 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_) { 2098 2099 List<T> res = new ArrayList<>(); 2100 2101 synchronized (lock) { 2102 2103 if (class_ == Resource.class || class_ == DomainResource.class || class_ == CanonicalResource.class || class_ == null) { 2104 res.addAll((List<T>) structures.getList()); 2105 res.addAll((List<T>) guides.getList()); 2106 res.addAll((List<T>) capstmts.getList()); 2107 res.addAll((List<T>) measures.getList()); 2108 res.addAll((List<T>) libraries.getList()); 2109 res.addAll((List<T>) valueSets.getList()); 2110 res.addAll((List<T>) codeSystems.getList()); 2111 res.addAll((List<T>) operations.getList()); 2112 res.addAll((List<T>) searchParameters.getList()); 2113 res.addAll((List<T>) plans.getList()); 2114 res.addAll((List<T>) maps.getList()); 2115 res.addAll((List<T>) transforms.getList()); 2116 res.addAll((List<T>) questionnaires.getList()); 2117 res.addAll((List<T>) systems.getList()); 2118 res.addAll((List<T>) actors.getList()); 2119 res.addAll((List<T>) requirements.getList()); 2120 } else if (class_ == ImplementationGuide.class) { 2121 res.addAll((List<T>) guides.getList()); 2122 } else if (class_ == CapabilityStatement.class) { 2123 res.addAll((List<T>) capstmts.getList()); 2124 } else if (class_ == Measure.class) { 2125 res.addAll((List<T>) measures.getList()); 2126 } else if (class_ == Library.class) { 2127 res.addAll((List<T>) libraries.getList()); 2128 } else if (class_ == StructureDefinition.class) { 2129 res.addAll((List<T>) structures.getList()); 2130 } else if (class_ == StructureMap.class) { 2131 res.addAll((List<T>) transforms.getList()); 2132 } else if (class_ == ValueSet.class) { 2133 res.addAll((List<T>) valueSets.getList()); 2134 } else if (class_ == CodeSystem.class) { 2135 res.addAll((List<T>) codeSystems.getList()); 2136 } else if (class_ == NamingSystem.class) { 2137 res.addAll((List<T>) systems.getList()); 2138 } else if (class_ == ActorDefinition.class) { 2139 res.addAll((List<T>) actors.getList()); 2140 } else if (class_ == Requirements.class) { 2141 res.addAll((List<T>) requirements.getList()); 2142 } else if (class_ == ConceptMap.class) { 2143 res.addAll((List<T>) maps.getList()); 2144 } else if (class_ == PlanDefinition.class) { 2145 res.addAll((List<T>) plans.getList()); 2146 } else if (class_ == OperationDefinition.class) { 2147 res.addAll((List<T>) operations.getList()); 2148 } else if (class_ == Questionnaire.class) { 2149 res.addAll((List<T>) questionnaires.getList()); 2150 } else if (class_ == SearchParameter.class) { 2151 res.addAll((List<T>) searchParameters.getList()); 2152 } 2153 } 2154 return res; 2155 } 2156 2157 private Set<String> notCanonical = new HashSet<String>(); 2158 2159 protected IWorkerContextManager.IPackageLoadingTracker packageTracker; 2160 private boolean forPublication; 2161 private boolean cachingAllowed = true; 2162 2163 @Override 2164 public Resource fetchResourceById(String type, String uri) { 2165 synchronized (lock) { 2166 String[] parts = uri.split("\\/"); 2167 if (!Utilities.noString(type) && parts.length == 1) { 2168 if (allResourcesById.containsKey(type)) { 2169 return allResourcesById.get(type).get(parts[0]).getResource(); 2170 } else { 2171 return null; 2172 } 2173 } 2174 if (parts.length >= 2) { 2175 if (!Utilities.noString(type)) { 2176 if (!type.equals(parts[parts.length-2])) { 2177 throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri)); 2178 } 2179 } 2180 return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource(); 2181 } else { 2182 throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri)); 2183 } 2184 } 2185 } 2186 2187 public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource sourceForReference) { 2188 try { 2189 return fetchResourceWithException(class_, uri, sourceForReference); 2190 } catch (FHIRException e) { 2191 throw new Error(e); 2192 } 2193 } 2194 2195 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 2196 try { 2197 return fetchResourceWithException(class_, uri, null); 2198 } catch (FHIRException e) { 2199 throw new Error(e); 2200 } 2201 } 2202 2203 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) { 2204 try { 2205 return fetchResourceWithExceptionByVersion(class_, uri, version, null); 2206 } catch (FHIRException e) { 2207 throw new Error(e); 2208 } 2209 } 2210 2211 @Override 2212 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 2213 try { 2214 return fetchResourceWithException(class_, uri) != null; 2215 } catch (Exception e) { 2216 return false; 2217 } 2218 } 2219 2220 public <T extends Resource> boolean hasResource(String cls, String uri) { 2221 try { 2222 return fetchResourceWithException(cls, uri) != null; 2223 } catch (Exception e) { 2224 return false; 2225 } 2226 } 2227 2228 public <T extends Resource> boolean hasResourceVersion(Class<T> class_, String uri, String version) { 2229 try { 2230 return fetchResourceWithExceptionByVersion(class_, uri, version, null) != null; 2231 } catch (Exception e) { 2232 return false; 2233 } 2234 } 2235 2236 public <T extends Resource> boolean hasResourceVersion(String cls, String uri, String version) { 2237 try { 2238 return fetchResourceWithExceptionByVersion(cls, uri, version, null) != null; 2239 } catch (Exception e) { 2240 return false; 2241 } 2242 } 2243 2244 2245 public TranslationServices translator() { 2246 return translator; 2247 } 2248 2249 public void setTranslator(TranslationServices translator) { 2250 this.translator = translator; 2251 } 2252 2253 public class NullTranslator implements TranslationServices { 2254 2255 @Override 2256 public String translate(String context, String value, String targetLang) { 2257 return value; 2258 } 2259 2260 @Override 2261 public String translate(String context, String value) { 2262 return value; 2263 } 2264 2265 @Override 2266 public String toStr(float value) { 2267 return null; 2268 } 2269 2270 @Override 2271 public String toStr(Date value) { 2272 return null; 2273 } 2274 2275 @Override 2276 public String translateAndFormat(String contest, String lang, String value, Object... args) { 2277 return String.format(value, args); 2278 } 2279 2280 @Override 2281 public Map<String, String> translations(String value) { 2282 // TODO Auto-generated method stub 2283 return null; 2284 } 2285 2286 @Override 2287 public Set<String> listTranslations(String category) { 2288 // TODO Auto-generated method stub 2289 return null; 2290 } 2291 2292 } 2293 2294 public void reportStatus(JsonObject json) { 2295 synchronized (lock) { 2296 json.addProperty("codeystem-count", codeSystems.size()); 2297 json.addProperty("valueset-count", valueSets.size()); 2298 json.addProperty("conceptmap-count", maps.size()); 2299 json.addProperty("transforms-count", transforms.size()); 2300 json.addProperty("structures-count", structures.size()); 2301 json.addProperty("guides-count", guides.size()); 2302 json.addProperty("statements-count", capstmts.size()); 2303 json.addProperty("measures-count", measures.size()); 2304 json.addProperty("libraries-count", libraries.size()); 2305 } 2306 } 2307 2308 2309 public void dropResource(Resource r) throws FHIRException { 2310 dropResource(r.fhirType(), r.getId()); 2311 } 2312 2313 public void dropResource(String fhirType, String id) { 2314 synchronized (lock) { 2315 2316 Map<String, ResourceProxy> map = allResourcesById.get(fhirType); 2317 if (map == null) { 2318 map = new HashMap<String, ResourceProxy>(); 2319 allResourcesById.put(fhirType, map); 2320 } 2321 if (map.containsKey(id)) { 2322 map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions) 2323 } 2324 2325 if (fhirType.equals("StructureDefinition")) { 2326 structures.drop(id); 2327 } else if (fhirType.equals("ImplementationGuide")) { 2328 guides.drop(id); 2329 } else if (fhirType.equals("CapabilityStatement")) { 2330 capstmts.drop(id); 2331 } else if (fhirType.equals("Measure")) { 2332 measures.drop(id); 2333 } else if (fhirType.equals("Library")) { 2334 libraries.drop(id); 2335 } else if (fhirType.equals("ValueSet")) { 2336 valueSets.drop(id); 2337 } else if (fhirType.equals("CodeSystem")) { 2338 codeSystems.drop(id); 2339 } else if (fhirType.equals("OperationDefinition")) { 2340 operations.drop(id); 2341 } else if (fhirType.equals("Questionnaire")) { 2342 questionnaires.drop(id); 2343 } else if (fhirType.equals("ConceptMap")) { 2344 maps.drop(id); 2345 } else if (fhirType.equals("StructureMap")) { 2346 transforms.drop(id); 2347 } else if (fhirType.equals("NamingSystem")) { 2348 systems.drop(id); 2349 systemUrlMap = null; 2350 } else if (fhirType.equals("ActorDefinition")) { 2351 actors.drop(id); 2352 } else if (fhirType.equals("Requirements")) { 2353 requirements.drop(id); 2354 } 2355 } 2356 } 2357 2358 private <T extends CanonicalResource> void dropMetadataResource(Map<String, T> map, String id) { 2359 T res = map.get(id); 2360 if (res != null) { 2361 map.remove(id); 2362 if (map.containsKey(res.getUrl())) { 2363 map.remove(res.getUrl()); 2364 } 2365 if (res.getVersion() != null) { 2366 if (map.containsKey(res.getUrl()+"|"+res.getVersion())) { 2367 map.remove(res.getUrl()+"|"+res.getVersion()); 2368 } 2369 } 2370 } 2371 } 2372 2373 2374 public String listSupportedSystems() { 2375 synchronized (lock) { 2376 String sl = null; 2377 for (String s : supportedCodeSystems) { 2378 sl = sl == null ? s : sl + "\r\n" + s; 2379 } 2380 return sl; 2381 } 2382 } 2383 2384 2385 public int totalCount() { 2386 synchronized (lock) { 2387 return valueSets.size() + maps.size() + structures.size() + transforms.size(); 2388 } 2389 } 2390 2391 public List<ConceptMap> listMaps() { 2392 List<ConceptMap> m = new ArrayList<ConceptMap>(); 2393 synchronized (lock) { 2394 maps.listAll(m); 2395 } 2396 return m; 2397 } 2398 2399 public List<StructureDefinition> listStructures() { 2400 List<StructureDefinition> m = new ArrayList<StructureDefinition>(); 2401 synchronized (lock) { 2402 structures.listAll(m); 2403 } 2404 return m; 2405 } 2406 2407 public StructureDefinition getStructure(String code) { 2408 synchronized (lock) { 2409 return structures.get(code); 2410 } 2411 } 2412 2413 private String getUri(NamingSystem ns) { 2414 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 2415 if (id.getType() == NamingSystemIdentifierType.URI) { 2416 return id.getValue(); 2417 } 2418 } 2419 return null; 2420 } 2421 2422 private boolean hasOid(NamingSystem ns, String oid) { 2423 for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { 2424 if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid)) { 2425 return true; 2426 } 2427 } 2428 return false; 2429 } 2430 2431 public void cacheVS(JsonObject json, Map<String, ValidationResult> t) { 2432 synchronized (lock) { 2433 validationCache.put(json.get("url").getAsString(), t); 2434 } 2435 } 2436 2437 public SearchParameter getSearchParameter(String code) { 2438 synchronized (lock) { 2439 return searchParameters.get(code); 2440 } 2441 } 2442 2443 @Override 2444 public ILoggingService getLogger() { 2445 return logger; 2446 } 2447 2448 @Override 2449 public StructureDefinition fetchTypeDefinition(String typeName) { 2450 if (Utilities.isAbsoluteUrl(typeName)) { 2451 return fetchResource(StructureDefinition.class, typeName); 2452 } else { 2453 Set<StructureDefinition> types = new HashSet<>(); 2454 types.addAll(fetchTypeDefinitions(typeName)); 2455 types.removeIf(sd -> sd.getDerivation() == TypeDerivationRule.CONSTRAINT); 2456 if (types.size() == 0) { 2457 return null; // throw new FHIRException("Unresolved type "+typeName+" (0)"); 2458 } else if (types.size() == 1) { 2459 return types.iterator().next(); 2460 } else { 2461 types.removeIf(sd -> !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")); 2462 if (types.size() != 1) { 2463 throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)"); 2464 } else { 2465 return types.iterator().next(); 2466 } 2467 } 2468 } 2469 } 2470 2471 @Override 2472 public List<StructureDefinition> fetchTypeDefinitions(String typeName) { 2473 List<StructureDefinition> res = new ArrayList<>(); 2474 structures.listAll(res); 2475 res.removeIf(sd -> !sd.hasType() || !(sd.getType().equals(typeName) || sd.getTypeTail().equals(typeName))); 2476 return res; 2477 } 2478 2479 public boolean isTlogging() { 2480 return tlogging; 2481 } 2482 2483 public void setTlogging(boolean tlogging) { 2484 this.tlogging = tlogging; 2485 } 2486 2487 public UcumService getUcumService() { 2488 return ucumService; 2489 } 2490 2491 public void setUcumService(UcumService ucumService) { 2492 this.ucumService = ucumService; 2493 } 2494 2495 public String getLinkForUrl(String corePath, String url) { 2496 if (url == null) { 2497 return null; 2498 } 2499 2500 if (codeSystems.has(url)) { 2501 return codeSystems.get(url).getWebPath(); 2502 } 2503 2504 if (valueSets.has(url)) { 2505 return valueSets.get(url).getWebPath(); 2506 } 2507 2508 if (maps.has(url)) { 2509 return maps.get(url).getWebPath(); 2510 } 2511 2512 if (transforms.has(url)) { 2513 return transforms.get(url).getWebPath(); 2514 } 2515 2516 if (actors.has(url)) { 2517 return actors.get(url).getWebPath(); 2518 } 2519 2520 if (requirements.has(url)) { 2521 return requirements.get(url).getWebPath(); 2522 } 2523 2524 if (structures.has(url)) { 2525 return structures.get(url).getWebPath(); 2526 } 2527 2528 if (guides.has(url)) { 2529 return guides.get(url).getWebPath(); 2530 } 2531 2532 if (capstmts.has(url)) { 2533 return capstmts.get(url).getWebPath(); 2534 } 2535 2536 if (measures.has(url)) { 2537 return measures.get(url).getWebPath(); 2538 } 2539 2540 if (libraries.has(url)) { 2541 return libraries.get(url).getWebPath(); 2542 } 2543 2544 if (searchParameters.has(url)) { 2545 return searchParameters.get(url).getWebPath(); 2546 } 2547 2548 if (questionnaires.has(url)) { 2549 return questionnaires.get(url).getWebPath(); 2550 } 2551 2552 if (operations.has(url)) { 2553 return operations.get(url).getWebPath(); 2554 } 2555 2556 if (plans.has(url)) { 2557 return plans.get(url).getWebPath(); 2558 } 2559 2560 if (url.equals("http://loinc.org")) { 2561 return corePath+"loinc.html"; 2562 } 2563 if (url.equals("http://unitsofmeasure.org")) { 2564 return corePath+"ucum.html"; 2565 } 2566 if (url.equals("http://snomed.info/sct")) { 2567 return corePath+"snomed.html"; 2568 } 2569 return null; 2570 } 2571 2572 public List<ImplementationGuide> allImplementationGuides() { 2573 List<ImplementationGuide> res = new ArrayList<>(); 2574 guides.listAll(res); 2575 return res; 2576 } 2577 2578 @Override 2579 public Set<String> getBinaryKeysAsSet() { return binaries.keySet(); } 2580 2581 @Override 2582 public boolean hasBinaryKey(String binaryKey) { 2583 return binaries.containsKey(binaryKey); 2584 } 2585 2586 @Override 2587 public byte[] getBinaryForKey(String binaryKey) { 2588 return binaries.get(binaryKey); 2589 } 2590 2591 public void finishLoading(boolean genSnapshots) { 2592 if (!hasResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Base")) { 2593 cacheResource(ProfileUtilities.makeBaseDefinition(version)); 2594 } 2595 if(genSnapshots) { 2596 for (StructureDefinition sd : listStructures()) { 2597 try { 2598 if (sd.getSnapshot().isEmpty()) { 2599 new ContextUtilities(this).generateSnapshot(sd); 2600 // new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd); 2601 } 2602 } catch (Exception e) { 2603 System.out.println("Unable to generate snapshot @1 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage()); 2604 if (logger.isDebugLogging()) { 2605 e.printStackTrace(); 2606 } 2607 } 2608 } 2609 } 2610 codeSystems.setVersion(version); 2611 valueSets.setVersion(version); 2612 maps.setVersion(version); 2613 transforms.setVersion(version); 2614 structures.setVersion(version); 2615 measures.setVersion(version); 2616 libraries.setVersion(version); 2617 guides.setVersion(version); 2618 capstmts.setVersion(version); 2619 searchParameters.setVersion(version); 2620 questionnaires.setVersion(version); 2621 operations.setVersion(version); 2622 plans.setVersion(version); 2623 systems.setVersion(version); 2624 actors.setVersion(version); 2625 requirements.setVersion(version); 2626 } 2627 2628 protected String tail(String url) { 2629 if (Utilities.noString(url)) { 2630 return "noname"; 2631 } 2632 if (url.contains("/")) { 2633 return url.substring(url.lastIndexOf("/")+1); 2634 } 2635 return url; 2636 } 2637 2638 public int getClientRetryCount() { 2639 return tcc.getClient() == null ? 0 : tcc.getClient().getRetryCount(); 2640 } 2641 2642 public IWorkerContext setClientRetryCount(int value) { 2643 if (tcc.getClient() != null) { 2644 tcc.getClient().setRetryCount(value); 2645 } 2646 return this; 2647 } 2648 2649 public ITerminologyClient getTxClient() { 2650 return tcc.getClient(); 2651 } 2652 2653 public String getCacheId() { 2654 return tcc.getCacheId(); 2655 } 2656 2657 public void setCacheId(String cacheId) { 2658 tcc.setCacheId(cacheId); 2659 } 2660 2661 public TerminologyCapabilities getTxCaps() { 2662 return tcc.getTxcaps(); 2663 } 2664 2665 public void setTxCaps(TerminologyCapabilities txCaps) { 2666 this.tcc.setTxcaps(txCaps); 2667 if (txCaps != null) { 2668 for (TerminologyCapabilitiesExpansionParameterComponent t : tcc.getTxcaps().getExpansion().getParameter()) { 2669 if ("cache-id".equals(t.getName())) { 2670 tcc.setTxCaching(true); 2671 } 2672 } 2673 for (TerminologyCapabilitiesCodeSystemComponent tccs : tcc.getTxcaps().getCodeSystem()) { 2674 supportedCodeSystems.add(tccs.getUri()); 2675 } 2676 } 2677 } 2678 2679 public TimeTracker clock() { 2680 return clock; 2681 } 2682 2683 2684 public int countAllCaches() { 2685 return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() + 2686 guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() + 2687 systems.size()+ actors.size()+ requirements.size(); 2688 } 2689 2690 public Set<String> getCodeSystemsUsed() { 2691 return codeSystemsUsed ; 2692 } 2693 2694 public IWorkerContextManager.ICanonicalResourceLocator getLocator() { 2695 return locator; 2696 } 2697 2698 public void setLocator(IWorkerContextManager.ICanonicalResourceLocator locator) { 2699 this.locator = locator; 2700 } 2701 2702 public String getUserAgent() { 2703 return userAgent; 2704 } 2705 2706 protected void setUserAgent(String userAgent) { 2707 this.userAgent = userAgent; 2708 if (tcc.getClient() != null) 2709 tcc.getClient().setUserAgent(userAgent); 2710 } 2711 2712 2713 public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() { 2714 return packageTracker; 2715 } 2716 2717 public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker packageTracker) { 2718 this.packageTracker = packageTracker; 2719 return this; 2720 } 2721 2722 2723 @Override 2724 public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps) { 2725 // TODO Auto-generated method stub 2726 return new PEBuilder(this, elementProps, fixedProps); 2727 } 2728 2729 public boolean isForPublication() { 2730 return forPublication; 2731 } 2732 2733 public void setForPublication(boolean value) { 2734 forPublication = value; 2735 } 2736 2737 public boolean isCachingAllowed() { 2738 return cachingAllowed; 2739 } 2740 2741 public void setCachingAllowed(boolean cachingAllowed) { 2742 this.cachingAllowed = cachingAllowed; 2743 } 2744 2745}