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