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