001package org.hl7.fhir.dstu3.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.hl7.fhir.dstu3.context.IWorkerContext;
015import org.hl7.fhir.dstu3.formats.IParser;
016import org.hl7.fhir.dstu3.formats.ParserType;
017import org.hl7.fhir.dstu3.model.CodeSystem;
018import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
019import org.hl7.fhir.dstu3.model.CodeableConcept;
020import org.hl7.fhir.dstu3.model.Coding;
021import org.hl7.fhir.dstu3.model.ConceptMap;
022import org.hl7.fhir.dstu3.model.ExpansionProfile;
023import org.hl7.fhir.dstu3.model.MetadataResource;
024import org.hl7.fhir.dstu3.model.Resource;
025import org.hl7.fhir.dstu3.model.ResourceType;
026import org.hl7.fhir.dstu3.model.StructureDefinition;
027import org.hl7.fhir.dstu3.model.ValueSet;
028import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
029import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
030import org.hl7.fhir.dstu3.terminologies.ValueSetExpander;
031import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
032import org.hl7.fhir.dstu3.utils.validation.IResourceValidator;
033import org.hl7.fhir.exceptions.FHIRException;
034import org.hl7.fhir.utilities.i18n.I18nBase;
035import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
036import org.hl7.fhir.utilities.validation.ValidationOptions;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
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;
046
047import static org.apache.commons.lang3.StringUtils.isNotBlank;
048
049public final class HapiWorkerContext extends I18nBase implements IWorkerContext {
050  private static final Logger ourLog = LoggerFactory.getLogger(HapiWorkerContext.class);
051  private final FhirContext myCtx;
052  private final Cache<String, Resource> myFetchedResourceCache;
053  private IValidationSupport myValidationSupport;
054  private ExpansionProfile myExpansionProfile;
055
056  public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
057    Validate.notNull(theCtx, "theCtx must not be null");
058    Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
059    myCtx = theCtx;
060    myValidationSupport = theValidationSupport;
061
062    long timeoutMillis = HapiSystemProperties.getTestValidationResourceCachesMs();
063    myFetchedResourceCache = CacheFactory.build(timeoutMillis);
064    // Set a default locale
065    setValidationMessageLanguage(getLocale());
066  }
067
068  @Override
069  @CoverageIgnore
070  public List<MetadataResource> allConformanceResources() {
071    throw new UnsupportedOperationException(Msg.code(610));
072  }
073
074  @Override
075  public List<StructureDefinition> allStructures() {
076    return myValidationSupport.fetchAllStructureDefinitions();
077  }
078
079  @Override
080  public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc, boolean theHierarchical) {
081    ValueSet input = new ValueSet();
082    input.getCompose().addInclude(theInc);
083    IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input);
084    ValueSet outputValueSet = (ValueSet) output.getValueSet();
085    if (outputValueSet != null) {
086      return outputValueSet.getExpansion();
087    } else {
088      return null;
089    }
090  }
091
092  @Override
093  public StructureDefinition fetchTypeDefinition(String theCode) {
094    return fetchResource(org.hl7.fhir.dstu3.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + theCode);
095  }
096
097  @Override
098  public CodeSystem fetchCodeSystem(String theSystem) {
099    if (myValidationSupport == null) {
100      return null;
101    } else {
102      return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
103    }
104  }
105
106  @Override
107  public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) {
108    Validate.notBlank(theUri, "theUri must not be null or blank");
109    if (myValidationSupport == null) {
110      return null;
111    } else {
112      try {
113        //noinspection unchecked
114        return (T) myFetchedResourceCache.get(theUri, t -> {
115          T resource = myValidationSupport.fetchResource(theClass, theUri);
116          if (resource == null) {
117            throw new IllegalArgumentException(Msg.code(611));
118          }
119          return resource;
120        });
121      } catch (IllegalArgumentException e) {
122        return null;
123      }
124    }
125  }
126
127  @Override
128  public <T extends Resource> T fetchResourceWithException(Class<T> theClass_, String theUri) throws FHIRException {
129    T retVal = fetchResource(theClass_, theUri);
130    if (retVal == null) {
131      throw new FHIRException(Msg.code(612) + "Unable to fetch " + theUri);
132    }
133    return retVal;
134  }
135
136  @Override
137  public List<ConceptMap> findMapsForSource(String theUrl) {
138    throw new UnsupportedOperationException(Msg.code(613));
139  }
140
141  @Override
142  public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical) {
143    throw new UnsupportedOperationException(Msg.code(614));
144  }
145
146  @Override
147  public String getAbbreviation(String theName) {
148    throw new UnsupportedOperationException(Msg.code(615));
149  }
150
151  @Override
152  public ExpansionProfile getExpansionProfile() {
153    return myExpansionProfile;
154  }
155
156  @Override
157  public void setExpansionProfile(ExpansionProfile theExpProfile) {
158    myExpansionProfile = theExpProfile;
159  }
160
161  @Override
162  public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) {
163    throw new UnsupportedOperationException(Msg.code(616));
164  }
165
166  @Override
167  public IResourceValidator newValidator() throws FHIRException {
168    throw new UnsupportedOperationException(Msg.code(617));
169  }
170
171  @Override
172  public IParser getParser(ParserType theType) {
173    throw new UnsupportedOperationException(Msg.code(618));
174  }
175
176  @Override
177  public IParser getParser(String theType) {
178    throw new UnsupportedOperationException(Msg.code(619));
179  }
180
181  @Override
182  public List<String> getResourceNames() {
183    List<String> result = new ArrayList<>();
184    for (ResourceType next : ResourceType.values()) {
185      result.add(next.name());
186    }
187    Collections.sort(result);
188    return result;
189  }
190
191  @Override
192  public Set<String> getResourceNamesAsSet() {
193    return new HashSet<>(getResourceNames());
194  }
195
196  @Override
197  public List<String> getTypeNames() {
198    throw new UnsupportedOperationException(Msg.code(620));
199  }
200
201  @Override
202  public String getVersion() {
203    return myCtx.getVersion().getVersion().getFhirVersionString();
204  }
205
206  @Override
207  @CoverageIgnore
208  public boolean hasCache() {
209    throw new UnsupportedOperationException(Msg.code(621));
210  }
211
212  @Override
213  public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
214    throw new UnsupportedOperationException(Msg.code(622));
215  }
216
217  @Override
218  public boolean isNoTerminologyServer() {
219    return false;
220  }
221
222  @Override
223  public IParser newJsonParser() {
224    throw new UnsupportedOperationException(Msg.code(623));
225  }
226
227  @Override
228  public IParser newXmlParser() {
229    throw new UnsupportedOperationException(Msg.code(624));
230  }
231
232  @Override
233  public String oid2Uri(String theCode) {
234    throw new UnsupportedOperationException(Msg.code(625));
235  }
236
237  @Override
238  public void setLogger(ILoggingService theLogger) {
239    throw new UnsupportedOperationException(Msg.code(626));
240  }
241
242  @Override
243  public boolean supportsSystem(String theSystem) {
244    if (myValidationSupport == null) {
245      return false;
246    } else {
247      return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem);
248    }
249  }
250
251  @Override
252  public Set<String> typeTails() {
253    return new HashSet<>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code",
254      "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint",
255      "Timing", "Reference", "Annotation", "Signature", "Meta"));
256  }
257
258  @Override
259  public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) {
260    for (Coding next : theCode.getCoding()) {
261      ValidationResult retVal = validateCode(next, theVs);
262      if (retVal.isOk()) {
263        return retVal;
264      }
265    }
266
267    return new ValidationResult(IssueSeverity.ERROR, null);
268  }
269
270  @Override
271  public ValidationResult validateCode(Coding theCode, ValueSet theVs) {
272    String system = theCode.getSystem();
273    String code = theCode.getCode();
274    String display = theCode.getDisplay();
275    return validateCode(system, code, display, theVs);
276  }
277
278  @Override
279  public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
280    ValidationOptions options = new ValidationOptions();
281    IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(options), theSystem, theCode, theDisplay, null);
282    if (result == null) {
283      return null;
284    }
285
286    IssueSeverity severity = null;
287    if (result.getSeverity() != null) {
288      severity = IssueSeverity.fromCode(result.getSeverityCode());
289    }
290    ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode());
291    return new ValidationResult(severity, result.getMessage(), definition);
292  }
293
294  public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
295    ConceptValidationOptions retVal = new ConceptValidationOptions();
296    if (theOptions.isGuessSystem()) {
297      retVal = retVal.setInferSystem(true);
298    }
299    return retVal;
300  }
301
302  @Override
303  public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
304    throw new UnsupportedOperationException(Msg.code(627));
305  }
306
307  @Override
308  public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
309
310    IValidationSupport.CodeValidationResult outcome;
311    ValidationOptions options = new ValidationOptions();
312    if (isNotBlank(theVs.getUrl())) {
313      outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(options), theSystem, theCode, theDisplay, theVs.getUrl());
314    } else {
315      outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(options), theSystem, theCode, theDisplay, theVs);
316    }
317
318    if (outcome != null && outcome.isOk()) {
319      ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
320      definition.setCode(theCode);
321      definition.setDisplay(outcome.getDisplay());
322      return new ValidationResult(definition);
323    }
324
325    return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) + "]");
326  }
327
328}