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