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