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(
151                                        new ValidationSupportContext(myValidationSupport), theSystem);
152                }
153        }
154
155        @Override
156        public Set<String> typeTails() {
157                return new HashSet<>(Arrays.asList(
158                                "Integer",
159                                "UnsignedInt",
160                                "PositiveInt",
161                                "Decimal",
162                                "DateTime",
163                                "Date",
164                                "Time",
165                                "Instant",
166                                "String",
167                                "Uri",
168                                "Oid",
169                                "Uuid",
170                                "Id",
171                                "Boolean",
172                                "Code",
173                                "Markdown",
174                                "Base64Binary",
175                                "Coding",
176                                "CodeableConcept",
177                                "Attachment",
178                                "Identifier",
179                                "Quantity",
180                                "SampledData",
181                                "Range",
182                                "Period",
183                                "Ratio",
184                                "HumanName",
185                                "Address",
186                                "ContactPoint",
187                                "Timing",
188                                "Reference",
189                                "Annotation",
190                                "Signature",
191                                "Meta"));
192        }
193
194        @Override
195        public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) {
196                for (Coding next : theCode.getCoding()) {
197                        ValidationResult retVal = validateCode(theOptions, next, theVs);
198                        if (retVal.isOk()) {
199                                return retVal;
200                        }
201                }
202
203                return new ValidationResult(IssueSeverity.ERROR, null);
204        }
205
206        @Override
207        public ValidationResult validateCode(ValidationOptions theOptions, Coding theCode, ValueSet theVs) {
208                String system = theCode.getSystem();
209                String code = theCode.getCode();
210                String display = theCode.getDisplay();
211                return validateCode(theOptions, system, code, display, theVs);
212        }
213
214        @Override
215        public ValidationResult validateCode(
216                        ValidationOptions theOptions, String theSystem, String theCode, String theDisplay) {
217                IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(
218                                new ValidationSupportContext(myValidationSupport),
219                                convertConceptValidationOptions(theOptions),
220                                theSystem,
221                                theCode,
222                                theDisplay,
223                                null);
224                if (result == null) {
225                        return null;
226                }
227
228                IssueSeverity severity = null;
229                if (result.getSeverity() != null) {
230                        severity = IssueSeverity.fromCode(result.getSeverityCode());
231                }
232
233                ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode());
234                return new ValidationResult(severity, result.getMessage(), definition);
235        }
236
237        @Override
238        public ValidationResult validateCode(
239                        ValidationOptions theOptions,
240                        String theSystem,
241                        String theCode,
242                        String theDisplay,
243                        ConceptSetComponent theVsi) {
244                throw new UnsupportedOperationException(Msg.code(267));
245        }
246
247        @Override
248        public ValidationResult validateCode(
249                        ValidationOptions theOptions, String theSystem, String theCode, String theDisplay, ValueSet theVs) {
250
251                IValidationSupport.CodeValidationResult outcome;
252                if (isNotBlank(theVs.getUrl())) {
253                        outcome = myValidationSupport.validateCode(
254                                        new ValidationSupportContext(myValidationSupport),
255                                        convertConceptValidationOptions(theOptions),
256                                        theSystem,
257                                        theCode,
258                                        theDisplay,
259                                        theVs.getUrl());
260                } else {
261                        outcome = myValidationSupport.validateCodeInValueSet(
262                                        new ValidationSupportContext(myValidationSupport),
263                                        convertConceptValidationOptions(theOptions),
264                                        theSystem,
265                                        theCode,
266                                        theDisplay,
267                                        theVs);
268                }
269
270                if (outcome != null && outcome.isOk()) {
271                        ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
272                        definition.setCode(theCode);
273                        definition.setDisplay(outcome.getDisplay());
274                        return new ValidationResult(definition);
275                }
276
277                return new ValidationResult(
278                                IssueSeverity.ERROR,
279                                "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem)
280                                                + "]");
281        }
282
283        @Override
284        public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) {
285                ValidationOptions options = theOptions.withGuessSystem();
286                return validateCode(options, null, code, null, vs);
287        }
288
289        @Override
290        @CoverageIgnore
291        public List<MetadataResource> allConformanceResources() {
292                throw new UnsupportedOperationException(Msg.code(268));
293        }
294
295        @Override
296        public void generateSnapshot(StructureDefinition p) throws FHIRException {
297                throw new UnsupportedOperationException(Msg.code(269));
298        }
299
300        @Override
301        public Parameters getExpansionParameters() {
302                return myExpansionProfile;
303        }
304
305        @Override
306        public void setExpansionProfile(Parameters theExpParameters) {
307                myExpansionProfile = theExpParameters;
308        }
309
310        @Override
311        @CoverageIgnore
312        public boolean hasCache() {
313                throw new UnsupportedOperationException(Msg.code(270));
314        }
315
316        @Override
317        public ValueSetExpander.ValueSetExpansionOutcome expandVS(
318                        ValueSet theSource, boolean theCacheOk, boolean theHierarchical) {
319                throw new UnsupportedOperationException(Msg.code(271));
320        }
321
322        @Override
323        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHierarchical)
324                        throws TerminologyServiceException {
325                ValueSet input = new ValueSet();
326                input.getCompose().addInclude(theInc);
327                IValidationSupport.ValueSetExpansionOutcome output =
328                                myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input);
329                return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null);
330        }
331
332        @Override
333        public ILoggingService getLogger() {
334                throw new UnsupportedOperationException(Msg.code(272));
335        }
336
337        @Override
338        public void setLogger(ILoggingService theLogger) {
339                throw new UnsupportedOperationException(Msg.code(273));
340        }
341
342        @Override
343        public String getVersion() {
344                return myCtx.getVersion().getVersion().getFhirVersionString();
345        }
346
347        @Override
348        public UcumService getUcumService() {
349                throw new UnsupportedOperationException(Msg.code(274));
350        }
351
352        @Override
353        public void setUcumService(UcumService ucumService) {
354                throw new UnsupportedOperationException(Msg.code(275));
355        }
356
357        @Override
358        public boolean isNoTerminologyServer() {
359                return false;
360        }
361
362        @Override
363        public TranslationServices translator() {
364                throw new UnsupportedOperationException(Msg.code(276));
365        }
366
367        @Override
368        public List<StructureMap> listTransforms() {
369                throw new UnsupportedOperationException(Msg.code(277));
370        }
371
372        @Override
373        public StructureMap getTransform(String url) {
374                throw new UnsupportedOperationException(Msg.code(278));
375        }
376
377        @Override
378        public String getOverrideVersionNs() {
379                return myOverrideVersionNs;
380        }
381
382        @Override
383        public void setOverrideVersionNs(String value) {
384                myOverrideVersionNs = value;
385        }
386
387        @Override
388        public StructureDefinition fetchTypeDefinition(String theTypeName) {
389                return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + theTypeName);
390        }
391
392        @Override
393        public String getLinkForUrl(String corePath, String url) {
394                throw new UnsupportedOperationException(Msg.code(279));
395        }
396
397        @Override
398        public List<String> getTypeNames() {
399                throw new UnsupportedOperationException(Msg.code(280));
400        }
401
402        @Override
403        public <T extends org.hl7.fhir.r4.model.Resource> T fetchResource(Class<T> theClass, String theUri) {
404                if (myValidationSupport == null || theUri == null) {
405                        return null;
406                } else {
407                        @SuppressWarnings("unchecked")
408                        T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri));
409                        return retVal;
410                }
411        }
412
413        @Override
414        public <T extends org.hl7.fhir.r4.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri)
415                        throws FHIRException {
416                T retVal = fetchResource(theClass, theUri);
417                if (retVal == null) {
418                        throw new FHIRException(Msg.code(281) + "Could not find resource: " + theUri);
419                }
420                return retVal;
421        }
422
423        @Override
424        public org.hl7.fhir.r4.model.Resource fetchResourceById(String theType, String theUri) {
425                throw new UnsupportedOperationException(Msg.code(282));
426        }
427
428        @Override
429        public <T extends org.hl7.fhir.r4.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) {
430                throw new UnsupportedOperationException(Msg.code(283));
431        }
432
433        @Override
434        public void cacheResource(org.hl7.fhir.r4.model.Resource theRes) throws FHIRException {
435                throw new UnsupportedOperationException(Msg.code(284));
436        }
437
438        @Override
439        public Set<String> getResourceNamesAsSet() {
440                return myCtx.getResourceTypes();
441        }
442
443        @Override
444        public ValueSetExpander.ValueSetExpansionOutcome expandVS(
445                        ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical)
446                        throws FHIRException {
447                throw new UnsupportedOperationException(Msg.code(285));
448        }
449
450        public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
451                ConceptValidationOptions retVal = new ConceptValidationOptions();
452                if (theOptions.isGuessSystem()) {
453                        retVal = retVal.setInferSystem(true);
454                }
455                return retVal;
456        }
457}