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