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