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