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