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, boolean theNoInactive) throws TerminologyServiceException { 285 ValueSet input = new ValueSet(); 286 input.getCompose().setInactive(!theNoInactive); //TODO GGG/DO is this valid? 287 input.getCompose().addInclude(theInc); 288 IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input); 289 return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null); 290 } 291 292 @Override 293 public Locale getLocale() { 294 return Locale.getDefault(); 295 } 296 297 @Override 298 public void setLocale(Locale locale) { 299 // ignore 300 } 301 302 @Override 303 public ILoggingService getLogger() { 304 throw new UnsupportedOperationException(Msg.code(213)); 305 } 306 307 @Override 308 public void setLogger(ILoggingService theLogger) { 309 throw new UnsupportedOperationException(Msg.code(214)); 310 } 311 312 @Override 313 public String getVersion() { 314 return myCtx.getVersion().getVersion().getFhirVersionString(); 315 } 316 317 @Override 318 public String getSpecUrl() { 319 throw new UnsupportedOperationException(Msg.code(215)); 320 } 321 322 @Override 323 public UcumService getUcumService() { 324 throw new UnsupportedOperationException(Msg.code(216)); 325 } 326 327 @Override 328 public void setUcumService(UcumService ucumService) { 329 throw new UnsupportedOperationException(Msg.code(217)); 330 } 331 332 @Override 333 public boolean isNoTerminologyServer() { 334 return false; 335 } 336 337 @Override 338 public Set<String> getCodeSystemsUsed() { 339 throw new UnsupportedOperationException(Msg.code(218)); 340 } 341 342 @Override 343 public TranslationServices translator() { 344 throw new UnsupportedOperationException(Msg.code(219)); 345 } 346 347 @Override 348 public List<StructureMap> listTransforms() { 349 throw new UnsupportedOperationException(Msg.code(220)); 350 } 351 352 @Override 353 public StructureMap getTransform(String url) { 354 throw new UnsupportedOperationException(Msg.code(221)); 355 } 356 357 @Override 358 public String getOverrideVersionNs() { 359 return myOverrideVersionNs; 360 } 361 362 @Override 363 public void setOverrideVersionNs(String value) { 364 myOverrideVersionNs = value; 365 } 366 367 @Override 368 public StructureDefinition fetchTypeDefinition(String typeName) { 369 return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); 370 } 371 372 @Override 373 public StructureDefinition fetchRawProfile(String url) { 374 throw new UnsupportedOperationException(Msg.code(222)); 375 } 376 377 @Override 378 public List<String> getTypeNames() { 379 throw new UnsupportedOperationException(Msg.code(223)); 380 } 381 382 @Override 383 public <T extends org.hl7.fhir.r5.model.Resource> T fetchResource(Class<T> theClass, String theUri) { 384 if (myValidationSupport == null || theUri == null) { 385 return null; 386 } else { 387 @SuppressWarnings("unchecked") 388 T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri)); 389 return retVal; 390 } 391 } 392 393 @Override 394 public <T extends org.hl7.fhir.r5.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException { 395 T retVal = fetchResource(theClass, theUri); 396 if (retVal == null) { 397 throw new FHIRException(Msg.code(224) + "Could not find resource: " + theUri); 398 } 399 return retVal; 400 } 401 402 @Override 403 public <T extends Resource> T fetchResource(Class<T> theClass, String theUri, String theVersion) { 404 return fetchResource(theClass, theUri + "|" + theVersion); 405 } 406 407 @Override 408 public <T extends Resource> T fetchResource(Class<T> class_, String uri, CanonicalResource canonicalForSource) { 409 throw new UnsupportedOperationException(Msg.code(225)); 410 } 411 412 @Override 413 public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) { 414 throw new UnsupportedOperationException(Msg.code(226)); 415 } 416 417 @Override 418 public <T extends org.hl7.fhir.r5.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) { 419 throw new UnsupportedOperationException(Msg.code(227)); 420 } 421 422 @Override 423 public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException { 424 throw new UnsupportedOperationException(Msg.code(228)); 425 } 426 427 @Override 428 public void cacheResourceFromPackage(Resource res, PackageVersion packageDetails) throws FHIRException { 429 throw new UnsupportedOperationException(Msg.code(229)); 430 } 431 432 @Override 433 public void cachePackage(PackageDetails packageDetails, List<PackageVersion> list) { 434 435 } 436 437 @Override 438 public Set<String> getResourceNamesAsSet() { 439 return myCtx.getResourceTypes(); 440 } 441 442 @Override 443 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) throws FHIRException { 444 throw new UnsupportedOperationException(Msg.code(230)); 445 } 446 447 448 @Override 449 public String getLinkForUrl(String corePath, String url) { 450 throw new UnsupportedOperationException(Msg.code(231)); 451 } 452 453 @Override 454 public Map<String, byte[]> getBinaries() { 455 throw new UnsupportedOperationException(Msg.code(232)); 456 } 457 458 @Override 459 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException { 460 throw new UnsupportedOperationException(Msg.code(233)); 461 } 462 463 @Override 464 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { 465 throw new UnsupportedOperationException(Msg.code(234)); 466 } 467 468 @Override 469 public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException { 470 throw new UnsupportedOperationException(Msg.code(235)); 471 } 472 473 @Override 474 public boolean hasPackage(String id, String ver) { 475 throw new UnsupportedOperationException(Msg.code(236)); 476 } 477 478 @Override 479 public boolean hasPackage(PackageVersion packageVersion) { 480 return false; 481 } 482 483 @Override 484 public PackageDetails getPackage(PackageVersion packageVersion) { 485 return null; 486 } 487 488 @Override 489 public int getClientRetryCount() { 490 throw new UnsupportedOperationException(Msg.code(237)); 491 } 492 493 @Override 494 public IWorkerContext setClientRetryCount(int value) { 495 throw new UnsupportedOperationException(Msg.code(238)); 496 } 497 498 @Override 499 public TimeTracker clock() { 500 return null; 501 } 502 503 @Override 504 public PackageVersion getPackageForUrl(String s) { 505 return null; 506 } 507 508 public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { 509 ConceptValidationOptions retVal = new ConceptValidationOptions(); 510 if (theOptions.isGuessSystem()) { 511 retVal = retVal.setInferSystem(true); 512 } 513 return retVal; 514 } 515 516}