001package org.hl7.fhir.r5.hapi.ctx;
002
003import ca.uhn.fhir.i18n.Msg;
004import ca.uhn.fhir.context.FhirContext;
005import ca.uhn.fhir.context.support.ConceptValidationOptions;
006import ca.uhn.fhir.context.support.IValidationSupport;
007import ca.uhn.fhir.context.support.ValidationSupportContext;
008import ca.uhn.fhir.rest.api.Constants;
009import ca.uhn.fhir.util.CoverageIgnore;
010import com.github.benmanes.caffeine.cache.Cache;
011import com.github.benmanes.caffeine.cache.Caffeine;
012import org.apache.commons.lang3.Validate;
013import org.apache.commons.lang3.time.DateUtils;
014import org.fhir.ucum.UcumService;
015import org.hl7.fhir.exceptions.FHIRException;
016import org.hl7.fhir.exceptions.TerminologyServiceException;
017import org.hl7.fhir.r5.context.IWorkerContext;
018import org.hl7.fhir.r5.formats.IParser;
019import org.hl7.fhir.r5.formats.ParserType;
020import org.hl7.fhir.r5.model.CanonicalResource;
021import org.hl7.fhir.r5.model.CodeSystem;
022import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
023import org.hl7.fhir.r5.model.CodeableConcept;
024import org.hl7.fhir.r5.model.Coding;
025import org.hl7.fhir.r5.model.ConceptMap;
026import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
027import org.hl7.fhir.r5.model.Parameters;
028import org.hl7.fhir.r5.model.Resource;
029import org.hl7.fhir.r5.model.ResourceType;
030import org.hl7.fhir.r5.model.StructureDefinition;
031import org.hl7.fhir.r5.model.StructureMap;
032import org.hl7.fhir.r5.model.ValueSet;
033import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
034import org.hl7.fhir.r5.terminologies.ValueSetExpander;
035import org.hl7.fhir.r5.utils.validation.IResourceValidator;
036import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
037import org.hl7.fhir.utilities.TimeTracker;
038import org.hl7.fhir.utilities.TranslationServices;
039import org.hl7.fhir.utilities.i18n.I18nBase;
040import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
041import org.hl7.fhir.utilities.npm.NpmPackage;
042import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
043import org.hl7.fhir.utilities.validation.ValidationOptions;
044
045import java.util.ArrayList;
046import java.util.Collections;
047import java.util.List;
048import java.util.Locale;
049import java.util.Map;
050import java.util.Set;
051import java.util.concurrent.TimeUnit;
052
053import static org.apache.commons.lang3.StringUtils.isNotBlank;
054
055public final class HapiWorkerContext extends I18nBase implements IWorkerContext {
056        private final FhirContext myCtx;
057        private final Cache<String, Resource> myFetchedResourceCache;
058        private final IValidationSupport myValidationSupport;
059        private Parameters myExpansionProfile;
060        private String myOverrideVersionNs;
061
062        public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
063                Validate.notNull(theCtx, "theCtx must not be null");
064                Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
065                myCtx = theCtx;
066                myValidationSupport = theValidationSupport;
067
068                long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND;
069                if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) {
070                        timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
071                }
072
073                myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
074
075                // Set a default locale
076                setValidationMessageLanguage(getLocale());
077        }
078
079        @Override
080        public List<StructureDefinition> allStructures() {
081                return myValidationSupport.fetchAllStructureDefinitions();
082        }
083
084        @Override
085        public List<StructureDefinition> getStructures() {
086                return allStructures();
087        }
088
089        @Override
090        public CodeSystem fetchCodeSystem(String theSystem) {
091                if (myValidationSupport == null) {
092                        return null;
093                } else {
094                        return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
095                }
096        }
097
098        @Override
099        public CodeSystem fetchCodeSystem(String theSystem, String version) {
100                if (myValidationSupport == null) {
101                        return null;
102                } else {
103                        return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
104                }
105        }
106
107        @Override
108        public List<ConceptMap> findMapsForSource(String theUrl) {
109                throw new UnsupportedOperationException(Msg.code(201));
110        }
111
112        @Override
113        public String getAbbreviation(String theName) {
114                throw new UnsupportedOperationException(Msg.code(202));
115        }
116
117        @Override
118        public IParser getParser(ParserType theType) {
119                throw new UnsupportedOperationException(Msg.code(203));
120        }
121
122        @Override
123        public IParser getParser(String theType) {
124                throw new UnsupportedOperationException(Msg.code(204));
125        }
126
127        @Override
128        public List<String> getResourceNames() {
129                List<String> result = new ArrayList<>();
130                for (ResourceType next : ResourceType.values()) {
131                        result.add(next.name());
132                }
133                Collections.sort(result);
134                return result;
135        }
136
137        @Override
138        public IParser newJsonParser() {
139                throw new UnsupportedOperationException(Msg.code(205));
140        }
141
142        @Override
143        public IResourceValidator newValidator() {
144                throw new UnsupportedOperationException(Msg.code(206));
145        }
146
147        @Override
148        public IParser newXmlParser() {
149                throw new UnsupportedOperationException(Msg.code(207));
150        }
151
152        @Override
153        public String oid2Uri(String theCode) {
154                throw new UnsupportedOperationException(Msg.code(208));
155        }
156
157        @Override
158        public boolean supportsSystem(String theSystem) {
159                if (myValidationSupport == null) {
160                        return false;
161                } else {
162                        return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem);
163                }
164        }
165
166
167        @Override
168        public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) {
169                for (Coding next : theCode.getCoding()) {
170                        ValidationResult retVal = validateCode(theOptions, next, theVs);
171                        if (retVal.isOk()) {
172                                return retVal;
173                        }
174                }
175
176                return new ValidationResult(IssueSeverity.ERROR, null);
177        }
178
179        @Override
180        public ValidationResult validateCode(ValidationOptions theOptions, Coding theCode, ValueSet theVs) {
181                String system = theCode.getSystem();
182                String code = theCode.getCode();
183                String display = theCode.getDisplay();
184                return validateCode(theOptions, system, null, code, display, theVs);
185        }
186
187        @Override
188        public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) {
189                return validateCode(options, code, vs);
190        }
191
192        @Override
193        public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) {
194                throw new UnsupportedOperationException(Msg.code(209));
195        }
196
197        @Override
198        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theValueSet, boolean cacheOk, boolean heiarchical, boolean incompleteOk) {
199                return null;
200        }
201
202        @Override
203        public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
204                                                                                                         String theCode, String theDisplay) {
205                IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
206                        convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null);
207                if (result == null) {
208                        return null;
209                }
210                IssueSeverity severity = null;
211                if (result.getSeverity() != null) {
212                        severity = IssueSeverity.fromCode(result.getSeverityCode());
213                }
214                ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode());
215                return new ValidationResult(severity, result.getMessage(), theSystem, definition);
216        }
217
218        @Override
219        public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
220                                                                                                         String theCode, String theDisplay, ValueSet theVs) {
221                IValidationSupport.CodeValidationResult outcome;
222                if (isNotBlank(theVs.getUrl())) {
223                        outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
224                                convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl());
225                } else {
226                        outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport),
227                                convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs);
228                }
229
230                if (outcome != null && outcome.isOk()) {
231                        ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
232                        definition.setCode(theCode);
233                        definition.setDisplay(outcome.getDisplay());
234                        return new ValidationResult(theSystem, definition);
235                }
236
237                return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" +
238                        Constants.codeSystemWithDefaultDescription(theSystem) + "]");
239        }
240
241        @Override
242        public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) {
243                return validateCode(theOptions, null, null, code, null, vs);
244        }
245
246        @Override
247        @CoverageIgnore
248        public List<CanonicalResource> allConformanceResources() {
249                throw new UnsupportedOperationException(Msg.code(210));
250        }
251
252        @Override
253        public void generateSnapshot(StructureDefinition p) throws FHIRException {
254                myValidationSupport.generateSnapshot(new ValidationSupportContext(myValidationSupport), p, "", "", "");
255        }
256
257        @Override
258        public void generateSnapshot(StructureDefinition mr, boolean ifLogical) {
259
260        }
261
262        @Override
263        public Parameters getExpansionParameters() {
264                return myExpansionProfile;
265        }
266
267        @Override
268        public void setExpansionProfile(Parameters theExpParameters) {
269                myExpansionProfile = theExpParameters;
270        }
271
272        @Override
273        @CoverageIgnore
274        public boolean hasCache() {
275                throw new UnsupportedOperationException(Msg.code(211));
276        }
277
278        @Override
279        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHierarchical) {
280                throw new UnsupportedOperationException(Msg.code(212));
281        }
282
283        @Override
284        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHierarchical) throws TerminologyServiceException {
285                ValueSet input = new ValueSet();
286                input.getCompose().addInclude(theInc);
287                IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input);
288                return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null);
289        }
290
291        @Override
292        public Locale getLocale() {
293                return Locale.getDefault();
294        }
295
296        @Override
297        public void setLocale(Locale locale) {
298                // ignore
299        }
300
301        @Override
302        public ILoggingService getLogger() {
303                throw new UnsupportedOperationException(Msg.code(213));
304        }
305
306        @Override
307        public void setLogger(ILoggingService theLogger) {
308                throw new UnsupportedOperationException(Msg.code(214));
309        }
310
311        @Override
312        public String getVersion() {
313                return myCtx.getVersion().getVersion().getFhirVersionString();
314        }
315
316        @Override
317        public String getSpecUrl() {
318                throw new UnsupportedOperationException(Msg.code(215));
319        }
320
321        @Override
322        public UcumService getUcumService() {
323                throw new UnsupportedOperationException(Msg.code(216));
324        }
325
326        @Override
327        public void setUcumService(UcumService ucumService) {
328                throw new UnsupportedOperationException(Msg.code(217));
329        }
330
331        @Override
332        public boolean isNoTerminologyServer() {
333                return false;
334        }
335
336        @Override
337        public Set<String> getCodeSystemsUsed() {
338                throw new UnsupportedOperationException(Msg.code(218));
339        }
340
341        @Override
342        public TranslationServices translator() {
343                throw new UnsupportedOperationException(Msg.code(219));
344        }
345
346        @Override
347        public List<StructureMap> listTransforms() {
348                throw new UnsupportedOperationException(Msg.code(220));
349        }
350
351        @Override
352        public StructureMap getTransform(String url) {
353                throw new UnsupportedOperationException(Msg.code(221));
354        }
355
356        @Override
357        public String getOverrideVersionNs() {
358                return myOverrideVersionNs;
359        }
360
361        @Override
362        public void setOverrideVersionNs(String value) {
363                myOverrideVersionNs = value;
364        }
365
366        @Override
367        public StructureDefinition fetchTypeDefinition(String typeName) {
368                return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
369        }
370
371        @Override
372        public StructureDefinition fetchRawProfile(String url) {
373                throw new UnsupportedOperationException(Msg.code(222));
374        }
375
376        @Override
377        public List<String> getTypeNames() {
378                throw new UnsupportedOperationException(Msg.code(223));
379        }
380
381        @Override
382        public <T extends org.hl7.fhir.r5.model.Resource> T fetchResource(Class<T> theClass, String theUri) {
383                if (myValidationSupport == null || theUri == null) {
384                        return null;
385                } else {
386                        @SuppressWarnings("unchecked")
387                        T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri));
388                        return retVal;
389                }
390        }
391
392        @Override
393        public <T extends org.hl7.fhir.r5.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException {
394                T retVal = fetchResource(theClass, theUri);
395                if (retVal == null) {
396                        throw new FHIRException(Msg.code(224) + "Could not find resource: " + theUri);
397                }
398                return retVal;
399        }
400
401        @Override
402        public <T extends Resource> T fetchResource(Class<T> theClass, String theUri, String theVersion) {
403                return fetchResource(theClass, theUri + "|" + theVersion);
404        }
405
406        @Override
407        public <T extends Resource> T fetchResource(Class<T> class_, String uri, CanonicalResource canonicalForSource) {
408                throw new UnsupportedOperationException(Msg.code(225));
409        }
410
411        @Override
412        public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) {
413                throw new UnsupportedOperationException(Msg.code(226));
414        }
415
416        @Override
417        public <T extends org.hl7.fhir.r5.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) {
418                throw new UnsupportedOperationException(Msg.code(227));
419        }
420
421        @Override
422        public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException {
423                throw new UnsupportedOperationException(Msg.code(228));
424        }
425
426        @Override
427        public void cacheResourceFromPackage(Resource res, PackageVersion packageDetails) throws FHIRException {
428                throw new UnsupportedOperationException(Msg.code(229));
429        }
430
431        @Override
432        public void cachePackage(PackageDetails packageDetails, List<PackageVersion> list) {
433
434        }
435
436        @Override
437        public Set<String> getResourceNamesAsSet() {
438                return myCtx.getResourceTypes();
439        }
440
441        @Override
442        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) throws FHIRException {
443                throw new UnsupportedOperationException(Msg.code(230));
444        }
445
446        @Override
447        public String getLinkForUrl(String corePath, String url) {
448                throw new UnsupportedOperationException(Msg.code(231));
449        }
450
451        @Override
452        public Map<String, byte[]> getBinaries() {
453                throw new UnsupportedOperationException(Msg.code(232));
454        }
455
456        @Override
457        public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException {
458                throw new UnsupportedOperationException(Msg.code(233));
459        }
460
461        @Override
462        public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException {
463                throw new UnsupportedOperationException(Msg.code(234));
464        }
465
466        @Override
467        public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException {
468                throw new UnsupportedOperationException(Msg.code(235));
469        }
470
471        @Override
472        public boolean hasPackage(String id, String ver) {
473                throw new UnsupportedOperationException(Msg.code(236));
474        }
475
476        @Override
477        public boolean hasPackage(PackageVersion packageVersion) {
478                return false;
479        }
480
481        @Override
482        public PackageDetails getPackage(PackageVersion packageVersion) {
483                return null;
484        }
485
486        @Override
487        public int getClientRetryCount() {
488                throw new UnsupportedOperationException(Msg.code(237));
489        }
490
491        @Override
492        public IWorkerContext setClientRetryCount(int value) {
493                throw new UnsupportedOperationException(Msg.code(238));
494        }
495
496        @Override
497        public TimeTracker clock() {
498                return null;
499        }
500
501        @Override
502        public PackageVersion getPackageForUrl(String s) {
503                return null;
504        }
505
506        public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
507                ConceptValidationOptions retVal = new ConceptValidationOptions();
508                if (theOptions.isGuessSystem()) {
509                        retVal = retVal.setInferSystem(true);
510                }
511                return retVal;
512        }
513
514}