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