001package org.hl7.fhir.r5.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 org.apache.commons.lang3.Validate;
013import org.fhir.ucum.UcumService;
014import org.hl7.fhir.exceptions.FHIRException;
015import org.hl7.fhir.exceptions.TerminologyServiceException;
016import org.hl7.fhir.r5.context.IWorkerContext;
017import org.hl7.fhir.r5.context.IWorkerContextManager;
018import org.hl7.fhir.r5.model.CodeSystem;
019import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
020import org.hl7.fhir.r5.model.CodeableConcept;
021import org.hl7.fhir.r5.model.Coding;
022import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
023import org.hl7.fhir.r5.model.NamingSystem;
024import org.hl7.fhir.r5.model.PackageInformation;
025import org.hl7.fhir.r5.model.Parameters;
026import org.hl7.fhir.r5.model.Resource;
027import org.hl7.fhir.r5.model.ResourceType;
028import org.hl7.fhir.r5.model.StructureDefinition;
029import org.hl7.fhir.r5.model.ValueSet;
030import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
031import org.hl7.fhir.r5.profilemodel.PEBuilder;
032import org.hl7.fhir.r5.terminologies.ValueSetExpander;
033import org.hl7.fhir.r5.utils.validation.IResourceValidator;
034import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
035import org.hl7.fhir.utilities.TimeTracker;
036import org.hl7.fhir.utilities.TranslationServices;
037import org.hl7.fhir.utilities.i18n.I18nBase;
038import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
039import org.hl7.fhir.utilities.npm.NpmPackage;
040import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
041import org.hl7.fhir.utilities.validation.ValidationOptions;
042
043import java.util.ArrayList;
044import java.util.Collections;
045import java.util.List;
046import java.util.Locale;
047import java.util.Map;
048import java.util.Set;
049
050import static org.apache.commons.lang3.StringUtils.isNotBlank;
051
052public final class HapiWorkerContext extends I18nBase implements IWorkerContext {
053        private final FhirContext myCtx;
054        private final Cache<String, Resource> myFetchedResourceCache;
055        private final IValidationSupport myValidationSupport;
056        private Parameters myExpansionProfile;
057        private String myOverrideVersionNs;
058
059        public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
060                Validate.notNull(theCtx, "theCtx must not be null");
061                Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
062                myCtx = theCtx;
063                myValidationSupport = theValidationSupport;
064
065                long timeoutMillis = HapiSystemProperties.getTestValidationResourceCachesMs();
066
067                myFetchedResourceCache = CacheFactory.build(timeoutMillis);
068
069                // Set a default locale
070                setValidationMessageLanguage(getLocale());
071        }
072
073        @Override
074        public CodeSystem fetchCodeSystem(String theSystem) {
075                if (myValidationSupport == null) {
076                        return null;
077                } else {
078                        return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
079                }
080        }
081
082        @Override
083        public CodeSystem fetchCodeSystem(String theSystem, String version) {
084                if (myValidationSupport == null) {
085                        return null;
086                } else {
087                        return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
088                }
089        }
090
091
092        @Override
093        public List<String> getResourceNames() {
094                List<String> result = new ArrayList<>();
095                for (ResourceType next : ResourceType.values()) {
096                        result.add(next.name());
097                }
098                Collections.sort(result);
099                return result;
100        }
101
102
103        @Override
104        public IResourceValidator newValidator() {
105                throw new UnsupportedOperationException(Msg.code(206));
106        }
107
108        @Override
109        public Map<String, NamingSystem> getNSUrlMap() {
110                throw new UnsupportedOperationException(Msg.code(2107));
111        }
112
113        @Override
114        public boolean supportsSystem(String theSystem) {
115                if (myValidationSupport == null) {
116                        return false;
117                } else {
118                        return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem);
119                }
120        }
121
122
123        @Override
124        public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) {
125                for (Coding next : theCode.getCoding()) {
126                        ValidationResult retVal = validateCode(theOptions, next, theVs);
127                        if (retVal.isOk()) {
128                                return retVal;
129                        }
130                }
131
132                return new ValidationResult(IssueSeverity.ERROR, null);
133        }
134
135        @Override
136        public ValidationResult validateCode(ValidationOptions theOptions, Coding theCode, ValueSet theVs) {
137                String system = theCode.getSystem();
138                String code = theCode.getCode();
139                String display = theCode.getDisplay();
140                return validateCode(theOptions, system, null, code, display, theVs);
141        }
142
143        @Override
144        public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) {
145                return validateCode(options, code, vs);
146        }
147
148        @Override
149        public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) {
150                throw new UnsupportedOperationException(Msg.code(209));
151        }
152
153        @Override
154        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theValueSet, boolean cacheOk, boolean heiarchical, boolean incompleteOk) {
155                return null;
156        }
157
158        @Override
159        public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
160                                                                                                         String theCode, String theDisplay) {
161                IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
162                        convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null);
163                if (result == null) {
164                        return null;
165                }
166                IssueSeverity severity = null;
167                if (result.getSeverity() != null) {
168                        severity = IssueSeverity.fromCode(result.getSeverityCode());
169                }
170                ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode());
171                return new ValidationResult(severity, result.getMessage(), theSystem, definition);
172        }
173
174        @Override
175        public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
176                                                                                                         String theCode, String theDisplay, ValueSet theVs) {
177                IValidationSupport.CodeValidationResult outcome;
178                if (isNotBlank(theVs.getUrl())) {
179                        outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
180                                convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl());
181                } else {
182                        outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport),
183                                convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs);
184                }
185
186                if (outcome != null && outcome.isOk()) {
187                        ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
188                        definition.setCode(theCode);
189                        definition.setDisplay(outcome.getDisplay());
190                        return new ValidationResult(theSystem, definition);
191                }
192
193                return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" +
194                        Constants.codeSystemWithDefaultDescription(theSystem) + "]");
195        }
196
197        @Override
198        public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) {
199                return validateCode(theOptions, null, null, code, null, vs);
200        }
201
202
203        @Override
204        public Parameters getExpansionParameters() {
205                return myExpansionProfile;
206        }
207
208        @Override
209        public void setExpansionProfile(Parameters theExpParameters) {
210                myExpansionProfile = theExpParameters;
211        }
212
213        @Override
214        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHierarchical) {
215                throw new UnsupportedOperationException(Msg.code(2128));
216        }
217
218        @Override
219        public ValueSetExpander.ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHierarchical, boolean theNoInactive) throws TerminologyServiceException {
220                ValueSet input = new ValueSet();
221                input.getCompose().setInactive(!theNoInactive); //TODO GGG/DO is this valid?
222                input.getCompose().addInclude(theInc);
223                IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input);
224                return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null);
225        }
226
227        @Override
228        public Locale getLocale() {
229                return Locale.getDefault();
230        }
231
232        @Override
233        public void setLocale(Locale locale) {
234                // ignore
235        }
236
237        @Override
238        public ILoggingService getLogger() {
239                throw new UnsupportedOperationException(Msg.code(213));
240        }
241
242        @Override
243        public void setLogger(ILoggingService theLogger) {
244                throw new UnsupportedOperationException(Msg.code(214));
245        }
246
247        @Override
248        public String getVersion() {
249                return myCtx.getVersion().getVersion().getFhirVersionString();
250        }
251
252
253        @Override
254        public UcumService getUcumService() {
255                throw new UnsupportedOperationException(Msg.code(216));
256        }
257
258        @Override
259        public void setUcumService(UcumService ucumService) {
260                throw new UnsupportedOperationException(Msg.code(217));
261        }
262
263        @Override
264        public boolean isNoTerminologyServer() {
265                return false;
266        }
267
268        @Override
269        public Set<String> getCodeSystemsUsed() {
270                throw new UnsupportedOperationException(Msg.code(218));
271        }
272
273        @Override
274        public TranslationServices translator() {
275                throw new UnsupportedOperationException(Msg.code(219));
276        }
277
278
279
280        @Override
281        public StructureDefinition fetchTypeDefinition(String typeName) {
282                return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
283        }
284
285
286        @Override
287        public <T extends org.hl7.fhir.r5.model.Resource> T fetchResource(Class<T> theClass, String theUri) {
288                if (myValidationSupport == null || theUri == null) {
289                        return null;
290                } else {
291                        @SuppressWarnings("unchecked")
292                        T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri));
293                        return retVal;
294                }
295        }
296
297        @Override
298        public <T extends org.hl7.fhir.r5.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException {
299                T retVal = fetchResource(theClass, theUri);
300                if (retVal == null) {
301                        throw new FHIRException(Msg.code(224) + "Could not find resource: " + theUri);
302                }
303                return retVal;
304        }
305
306        @Override
307        public <T extends Resource> T fetchResourceWithException(Class<T> theClass, String uri, Resource sourceOfReference) throws FHIRException {
308                throw new UnsupportedOperationException(Msg.code(2213));
309        }
310
311
312        @Override
313        public <T extends Resource> T fetchResource(Class<T> theClass, String theUri, String theVersion) {
314                return fetchResource(theClass, theUri + "|" + theVersion);
315        }
316
317        @Override
318        public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource canonicalForSource) {
319                return fetchResource(class_,uri);
320        }
321
322        @Override
323        public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) {
324                throw new UnsupportedOperationException(Msg.code(226));
325        }
326
327        @Override
328        public <T extends org.hl7.fhir.r5.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) {
329                throw new UnsupportedOperationException(Msg.code(227));
330        }
331
332        @Override
333        public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException {
334                throw new UnsupportedOperationException(Msg.code(228));
335        }
336
337        @Override
338        public void cacheResourceFromPackage(Resource res, PackageInformation packageDetails) throws FHIRException {
339                throw new UnsupportedOperationException(Msg.code(229));
340        }
341
342        @Override
343        public void cachePackage(PackageInformation packageInformation) {
344
345        }
346
347        @Override
348        public Set<String> getResourceNamesAsSet() {
349                return myCtx.getResourceTypes();
350        }
351
352
353        @Override
354        public ValueSetExpander.ValueSetExpansionOutcome expandVS(Resource src,ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) throws FHIRException {
355                throw new UnsupportedOperationException(Msg.code(230));
356        }
357
358
359
360        @Override
361        public Set<String> getBinaryKeysAsSet() {
362                throw new UnsupportedOperationException(Msg.code(2115));
363        }
364
365        @Override
366        public boolean hasBinaryKey(String s) {
367                throw new UnsupportedOperationException(Msg.code(2129));
368        }
369
370        @Override
371        public byte[] getBinaryForKey(String s) {
372                throw new UnsupportedOperationException(Msg.code(2199));
373        }
374
375        @Override
376        public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException {
377                throw new UnsupportedOperationException(Msg.code(233));
378        }
379
380        @Override
381        public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException {
382                throw new UnsupportedOperationException(Msg.code(234));
383        }
384
385        @Override
386        public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException {
387                throw new UnsupportedOperationException(Msg.code(235));
388        }
389
390        @Override
391        public boolean hasPackage(String id, String ver) {
392                throw new UnsupportedOperationException(Msg.code(236));
393        }
394
395        @Override
396        public boolean hasPackage(PackageInformation packageVersion) {
397                return false;
398        }
399
400        @Override
401        public PackageInformation getPackage(String id, String ver) {
402                return null;
403        }
404
405        @Override
406        public int getClientRetryCount() {
407                throw new UnsupportedOperationException(Msg.code(237));
408        }
409
410        @Override
411        public IWorkerContext setClientRetryCount(int value) {
412                throw new UnsupportedOperationException(Msg.code(238));
413        }
414
415        @Override
416        public TimeTracker clock() {
417                return null;
418        }
419
420        @Override
421        public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() {
422                throw new UnsupportedOperationException(Msg.code(2112));
423        }
424
425        @Override
426        public PackageInformation getPackageForUrl(String s) {
427                return null;
428        }
429
430        public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
431                ConceptValidationOptions retVal = new ConceptValidationOptions();
432                if (theOptions.isGuessSystem()) {
433                        retVal = retVal.setInferSystem(true);
434                }
435                return retVal;
436        }
437
438
439        @Override
440        public <T extends Resource> List<T> fetchResourcesByType(Class<T> theClass) {
441                if (theClass.equals(StructureDefinition.class)) {
442                        return myValidationSupport.fetchAllStructureDefinitions();
443                }
444
445                throw new UnsupportedOperationException(Msg.code(2113) + "Can't fetch all resources of type: " + theClass);
446        }
447
448        @Override
449        public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker theIPackageLoadingTracker) {
450                throw new UnsupportedOperationException(Msg.code(220));
451        }
452
453        @Override
454        public String getSpecUrl() {
455                return "";
456        }
457
458        @Override
459        public PEBuilder getProfiledElementBuilder(PEBuilder.PEElementPropertiesPolicy thePEElementPropertiesPolicy, boolean theB) {
460                throw new UnsupportedOperationException(Msg.code(2261));
461        }
462}