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