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}