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