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