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}