001package org.hl7.fhir.r5.hapi.ctx; 002 003import ca.uhn.fhir.i18n.Msg; 004import ca.uhn.fhir.context.FhirContext; 005import ca.uhn.fhir.context.support.ConceptValidationOptions; 006import ca.uhn.fhir.context.support.IValidationSupport; 007import ca.uhn.fhir.context.support.ValidationSupportContext; 008import ca.uhn.fhir.rest.api.Constants; 009import ca.uhn.fhir.util.CoverageIgnore; 010import com.github.benmanes.caffeine.cache.Cache; 011import com.github.benmanes.caffeine.cache.Caffeine; 012import org.apache.commons.lang3.Validate; 013import org.apache.commons.lang3.time.DateUtils; 014import org.fhir.ucum.UcumService; 015import org.hl7.fhir.exceptions.FHIRException; 016import org.hl7.fhir.exceptions.TerminologyServiceException; 017import org.hl7.fhir.r5.context.IWorkerContext; 018import org.hl7.fhir.r5.formats.IParser; 019import org.hl7.fhir.r5.formats.ParserType; 020import org.hl7.fhir.r5.model.CanonicalResource; 021import org.hl7.fhir.r5.model.CodeSystem; 022import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 023import org.hl7.fhir.r5.model.CodeableConcept; 024import org.hl7.fhir.r5.model.Coding; 025import org.hl7.fhir.r5.model.ConceptMap; 026import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 027import org.hl7.fhir.r5.model.Parameters; 028import org.hl7.fhir.r5.model.Resource; 029import org.hl7.fhir.r5.model.ResourceType; 030import org.hl7.fhir.r5.model.StructureDefinition; 031import org.hl7.fhir.r5.model.StructureMap; 032import org.hl7.fhir.r5.model.ValueSet; 033import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 034import org.hl7.fhir.r5.terminologies.ValueSetExpander; 035import org.hl7.fhir.r5.utils.validation.IResourceValidator; 036import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; 037import org.hl7.fhir.utilities.TimeTracker; 038import org.hl7.fhir.utilities.TranslationServices; 039import org.hl7.fhir.utilities.i18n.I18nBase; 040import org.hl7.fhir.utilities.npm.BasePackageCacheManager; 041import org.hl7.fhir.utilities.npm.NpmPackage; 042import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 043import org.hl7.fhir.utilities.validation.ValidationOptions; 044 045import java.util.ArrayList; 046import java.util.Collections; 047import java.util.List; 048import java.util.Locale; 049import java.util.Map; 050import java.util.Set; 051import java.util.concurrent.TimeUnit; 052 053import static org.apache.commons.lang3.StringUtils.isNotBlank; 054 055public final class HapiWorkerContext extends I18nBase implements IWorkerContext { 056 private final FhirContext myCtx; 057 private final Cache<String, Resource> myFetchedResourceCache; 058 private final IValidationSupport myValidationSupport; 059 private Parameters myExpansionProfile; 060 private String myOverrideVersionNs; 061 062 public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { 063 Validate.notNull(theCtx, "theCtx must not be null"); 064 Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); 065 myCtx = theCtx; 066 myValidationSupport = theValidationSupport; 067 068 long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND; 069 if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) { 070 timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)); 071 } 072 073 myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build(); 074 075 // Set a default locale 076 setValidationMessageLanguage(getLocale()); 077 } 078 079 @Override 080 public List<StructureDefinition> allStructures() { 081 return myValidationSupport.fetchAllStructureDefinitions(); 082 } 083 084 @Override 085 public List<StructureDefinition> getStructures() { 086 return allStructures(); 087 } 088 089 @Override 090 public CodeSystem fetchCodeSystem(String theSystem) { 091 if (myValidationSupport == null) { 092 return null; 093 } else { 094 return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); 095 } 096 } 097 098 @Override 099 public CodeSystem fetchCodeSystem(String theSystem, String version) { 100 if (myValidationSupport == null) { 101 return null; 102 } else { 103 return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); 104 } 105 } 106 107 @Override 108 public List<ConceptMap> findMapsForSource(String theUrl) { 109 throw new UnsupportedOperationException(Msg.code(201)); 110 } 111 112 @Override 113 public String getAbbreviation(String theName) { 114 throw new UnsupportedOperationException(Msg.code(202)); 115 } 116 117 @Override 118 public IParser getParser(ParserType theType) { 119 throw new UnsupportedOperationException(Msg.code(203)); 120 } 121 122 @Override 123 public IParser getParser(String theType) { 124 throw new UnsupportedOperationException(Msg.code(204)); 125 } 126 127 @Override 128 public List<String> getResourceNames() { 129 List<String> result = new ArrayList<>(); 130 for (ResourceType next : ResourceType.values()) { 131 result.add(next.name()); 132 } 133 Collections.sort(result); 134 return result; 135 } 136 137 @Override 138 public IParser newJsonParser() { 139 throw new UnsupportedOperationException(Msg.code(205)); 140 } 141 142 @Override 143 public IResourceValidator newValidator() { 144 throw new UnsupportedOperationException(Msg.code(206)); 145 } 146 147 @Override 148 public IParser newXmlParser() { 149 throw new UnsupportedOperationException(Msg.code(207)); 150 } 151 152 @Override 153 public String oid2Uri(String theCode) { 154 throw new UnsupportedOperationException(Msg.code(208)); 155 } 156 157 @Override 158 public boolean supportsSystem(String theSystem) { 159 if (myValidationSupport == null) { 160 return false; 161 } else { 162 return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem); 163 } 164 } 165 166 167 @Override 168 public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) { 169 for (Coding next : theCode.getCoding()) { 170 ValidationResult retVal = validateCode(theOptions, next, theVs); 171 if (retVal.isOk()) { 172 return retVal; 173 } 174 } 175 176 return new ValidationResult(IssueSeverity.ERROR, null); 177 } 178 179 @Override 180 public ValidationResult validateCode(ValidationOptions theOptions, Coding theCode, ValueSet theVs) { 181 String system = theCode.getSystem(); 182 String code = theCode.getCode(); 183 String display = theCode.getDisplay(); 184 return validateCode(theOptions, system, null, code, display, theVs); 185 } 186 187 @Override 188 public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) { 189 return validateCode(options, code, vs); 190 } 191 192 @Override 193 public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) { 194 throw new UnsupportedOperationException(Msg.code(209)); 195 } 196 197 @Override 198 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theValueSet, boolean cacheOk, boolean heiarchical, boolean incompleteOk) { 199 return null; 200 } 201 202 @Override 203 public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion, 204 String theCode, String theDisplay) { 205 IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), 206 convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null); 207 if (result == null) { 208 return null; 209 } 210 IssueSeverity severity = null; 211 if (result.getSeverity() != null) { 212 severity = IssueSeverity.fromCode(result.getSeverityCode()); 213 } 214 ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode()); 215 return new ValidationResult(severity, result.getMessage(), theSystem, definition); 216 } 217 218 @Override 219 public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion, 220 String theCode, String theDisplay, ValueSet theVs) { 221 IValidationSupport.CodeValidationResult outcome; 222 if (isNotBlank(theVs.getUrl())) { 223 outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), 224 convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl()); 225 } else { 226 outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport), 227 convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs); 228 } 229 230 if (outcome != null && outcome.isOk()) { 231 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 232 definition.setCode(theCode); 233 definition.setDisplay(outcome.getDisplay()); 234 return new ValidationResult(theSystem, definition); 235 } 236 237 return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + 238 Constants.codeSystemWithDefaultDescription(theSystem) + "]"); 239 } 240 241 @Override 242 public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) { 243 return validateCode(theOptions, null, null, code, null, vs); 244 } 245 246 @Override 247 @CoverageIgnore 248 public List<CanonicalResource> allConformanceResources() { 249 throw new UnsupportedOperationException(Msg.code(210)); 250 } 251 252 @Override 253 public void generateSnapshot(StructureDefinition p) throws FHIRException { 254 myValidationSupport.generateSnapshot(new ValidationSupportContext(myValidationSupport), p, "", "", ""); 255 } 256 257 @Override 258 public void generateSnapshot(StructureDefinition mr, boolean ifLogical) { 259 260 } 261 262 @Override 263 public Parameters getExpansionParameters() { 264 return myExpansionProfile; 265 } 266 267 @Override 268 public void setExpansionProfile(Parameters theExpParameters) { 269 myExpansionProfile = theExpParameters; 270 } 271 272 @Override 273 @CoverageIgnore 274 public boolean hasCache() { 275 throw new UnsupportedOperationException(Msg.code(211)); 276 } 277 278 @Override 279 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHierarchical) { 280 throw new UnsupportedOperationException(Msg.code(212)); 281 } 282 283 @Override 284 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHierarchical) throws TerminologyServiceException { 285 ValueSet input = new ValueSet(); 286 input.getCompose().addInclude(theInc); 287 IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input); 288 return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null); 289 } 290 291 @Override 292 public Locale getLocale() { 293 return Locale.getDefault(); 294 } 295 296 @Override 297 public void setLocale(Locale locale) { 298 // ignore 299 } 300 301 @Override 302 public ILoggingService getLogger() { 303 throw new UnsupportedOperationException(Msg.code(213)); 304 } 305 306 @Override 307 public void setLogger(ILoggingService theLogger) { 308 throw new UnsupportedOperationException(Msg.code(214)); 309 } 310 311 @Override 312 public String getVersion() { 313 return myCtx.getVersion().getVersion().getFhirVersionString(); 314 } 315 316 @Override 317 public String getSpecUrl() { 318 throw new UnsupportedOperationException(Msg.code(215)); 319 } 320 321 @Override 322 public UcumService getUcumService() { 323 throw new UnsupportedOperationException(Msg.code(216)); 324 } 325 326 @Override 327 public void setUcumService(UcumService ucumService) { 328 throw new UnsupportedOperationException(Msg.code(217)); 329 } 330 331 @Override 332 public boolean isNoTerminologyServer() { 333 return false; 334 } 335 336 @Override 337 public Set<String> getCodeSystemsUsed() { 338 throw new UnsupportedOperationException(Msg.code(218)); 339 } 340 341 @Override 342 public TranslationServices translator() { 343 throw new UnsupportedOperationException(Msg.code(219)); 344 } 345 346 @Override 347 public List<StructureMap> listTransforms() { 348 throw new UnsupportedOperationException(Msg.code(220)); 349 } 350 351 @Override 352 public StructureMap getTransform(String url) { 353 throw new UnsupportedOperationException(Msg.code(221)); 354 } 355 356 @Override 357 public String getOverrideVersionNs() { 358 return myOverrideVersionNs; 359 } 360 361 @Override 362 public void setOverrideVersionNs(String value) { 363 myOverrideVersionNs = value; 364 } 365 366 @Override 367 public StructureDefinition fetchTypeDefinition(String typeName) { 368 return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); 369 } 370 371 @Override 372 public StructureDefinition fetchRawProfile(String url) { 373 throw new UnsupportedOperationException(Msg.code(222)); 374 } 375 376 @Override 377 public List<String> getTypeNames() { 378 throw new UnsupportedOperationException(Msg.code(223)); 379 } 380 381 @Override 382 public <T extends org.hl7.fhir.r5.model.Resource> T fetchResource(Class<T> theClass, String theUri) { 383 if (myValidationSupport == null || theUri == null) { 384 return null; 385 } else { 386 @SuppressWarnings("unchecked") 387 T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri)); 388 return retVal; 389 } 390 } 391 392 @Override 393 public <T extends org.hl7.fhir.r5.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException { 394 T retVal = fetchResource(theClass, theUri); 395 if (retVal == null) { 396 throw new FHIRException(Msg.code(224) + "Could not find resource: " + theUri); 397 } 398 return retVal; 399 } 400 401 @Override 402 public <T extends Resource> T fetchResource(Class<T> theClass, String theUri, String theVersion) { 403 return fetchResource(theClass, theUri + "|" + theVersion); 404 } 405 406 @Override 407 public <T extends Resource> T fetchResource(Class<T> class_, String uri, CanonicalResource canonicalForSource) { 408 throw new UnsupportedOperationException(Msg.code(225)); 409 } 410 411 @Override 412 public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) { 413 throw new UnsupportedOperationException(Msg.code(226)); 414 } 415 416 @Override 417 public <T extends org.hl7.fhir.r5.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) { 418 throw new UnsupportedOperationException(Msg.code(227)); 419 } 420 421 @Override 422 public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException { 423 throw new UnsupportedOperationException(Msg.code(228)); 424 } 425 426 @Override 427 public void cacheResourceFromPackage(Resource res, PackageVersion packageDetails) throws FHIRException { 428 throw new UnsupportedOperationException(Msg.code(229)); 429 } 430 431 @Override 432 public void cachePackage(PackageDetails packageDetails, List<PackageVersion> list) { 433 434 } 435 436 @Override 437 public Set<String> getResourceNamesAsSet() { 438 return myCtx.getResourceTypes(); 439 } 440 441 @Override 442 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) throws FHIRException { 443 throw new UnsupportedOperationException(Msg.code(230)); 444 } 445 446 @Override 447 public String getLinkForUrl(String corePath, String url) { 448 throw new UnsupportedOperationException(Msg.code(231)); 449 } 450 451 @Override 452 public Map<String, byte[]> getBinaries() { 453 throw new UnsupportedOperationException(Msg.code(232)); 454 } 455 456 @Override 457 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException { 458 throw new UnsupportedOperationException(Msg.code(233)); 459 } 460 461 @Override 462 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { 463 throw new UnsupportedOperationException(Msg.code(234)); 464 } 465 466 @Override 467 public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException { 468 throw new UnsupportedOperationException(Msg.code(235)); 469 } 470 471 @Override 472 public boolean hasPackage(String id, String ver) { 473 throw new UnsupportedOperationException(Msg.code(236)); 474 } 475 476 @Override 477 public boolean hasPackage(PackageVersion packageVersion) { 478 return false; 479 } 480 481 @Override 482 public PackageDetails getPackage(PackageVersion packageVersion) { 483 return null; 484 } 485 486 @Override 487 public int getClientRetryCount() { 488 throw new UnsupportedOperationException(Msg.code(237)); 489 } 490 491 @Override 492 public IWorkerContext setClientRetryCount(int value) { 493 throw new UnsupportedOperationException(Msg.code(238)); 494 } 495 496 @Override 497 public TimeTracker clock() { 498 return null; 499 } 500 501 @Override 502 public PackageVersion getPackageForUrl(String s) { 503 return null; 504 } 505 506 public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { 507 ConceptValidationOptions retVal = new ConceptValidationOptions(); 508 if (theOptions.isGuessSystem()) { 509 retVal = retVal.setInferSystem(true); 510 } 511 return retVal; 512 } 513 514}