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