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