001package ca.uhn.fhir.rest.server.interceptor;
002
003/*-
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
024import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
025import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
026import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
027import ca.uhn.fhir.context.support.IValidationSupport;
028import ca.uhn.fhir.context.support.ValidationSupportContext;
029import ca.uhn.fhir.interceptor.api.Hook;
030import ca.uhn.fhir.interceptor.api.Pointcut;
031import ca.uhn.fhir.rest.api.server.RequestDetails;
032import ca.uhn.fhir.util.FhirTerser;
033import ca.uhn.fhir.util.IModelVisitor;
034import org.hl7.fhir.instance.model.api.IBase;
035import org.hl7.fhir.instance.model.api.IBaseResource;
036import org.hl7.fhir.instance.model.api.IPrimitiveType;
037
038import java.util.List;
039import java.util.Objects;
040
041import static ca.uhn.fhir.rest.server.interceptor.InterceptorOrders.RESPONSE_TERMINOLOGY_DISPLAY_POPULATION_INTERCEPTOR;
042import static org.apache.commons.lang3.StringUtils.isBlank;
043import static org.apache.commons.lang3.StringUtils.isNotBlank;
044
045/**
046 * This interceptor looks for coded data (
047 *
048 * @since 5.4.0
049 */
050public class ResponseTerminologyDisplayPopulationInterceptor extends BaseResponseTerminologyInterceptor {
051
052        private final BaseRuntimeChildDefinition myCodingSystemChild;
053        private final BaseRuntimeChildDefinition myCodingCodeChild;
054        private final Class<? extends IBase> myCodingType;
055        private final BaseRuntimeElementCompositeDefinition<?> myCodingDefinitition;
056        private final BaseRuntimeChildDefinition myCodingDisplayChild;
057        private final RuntimePrimitiveDatatypeDefinition myStringDefinition;
058
059        /**
060         * Constructor
061         *
062         * @param theValidationSupport The validation support module
063         */
064        public ResponseTerminologyDisplayPopulationInterceptor(IValidationSupport theValidationSupport) {
065                super(theValidationSupport);
066
067                myCodingDefinitition = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(myContext.getElementDefinition("Coding"));
068                myCodingType = myCodingDefinitition.getImplementingClass();
069                myCodingSystemChild = myCodingDefinitition.getChildByName("system");
070                myCodingCodeChild = myCodingDefinitition.getChildByName("code");
071                myCodingDisplayChild = myCodingDefinitition.getChildByName("display");
072
073                myStringDefinition = (RuntimePrimitiveDatatypeDefinition) myContext.getElementDefinition("string");
074        }
075
076        @Hook(value = Pointcut.SERVER_OUTGOING_RESPONSE, order = RESPONSE_TERMINOLOGY_DISPLAY_POPULATION_INTERCEPTOR)
077        public void handleResource(RequestDetails theRequestDetails, IBaseResource theResource) {
078                List<IBaseResource> resources = toListForProcessing(theRequestDetails, theResource);
079
080                FhirTerser terser = myContext.newTerser();
081                for (IBaseResource nextResource : resources) {
082                        terser.visit(nextResource, new MappingVisitor());
083                }
084
085        }
086
087        private class MappingVisitor implements IModelVisitor {
088
089                @Override
090                public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
091                        if (myCodingType.isAssignableFrom(theElement.getClass())) {
092                                String system = myCodingSystemChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
093                                String code = myCodingCodeChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
094                                if (isBlank(system) || isBlank(code)) {
095                                        return;
096                                }
097
098                                String display = myCodingDisplayChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
099                                if (isNotBlank(display)) {
100                                        return;
101                                }
102
103                                ValidationSupportContext validationSupportContext = new ValidationSupportContext(myValidationSupport);
104                                if (myValidationSupport.isCodeSystemSupported(validationSupportContext, system)) {
105
106                                        IValidationSupport.LookupCodeResult lookupCodeResult = myValidationSupport.lookupCode(validationSupportContext, system, code);
107                                        if (lookupCodeResult != null && lookupCodeResult.isFound()) {
108                                                String newDisplay = lookupCodeResult.getCodeDisplay();
109                                                IPrimitiveType<?> newString = myStringDefinition.newInstance(newDisplay);
110                                                myCodingDisplayChild.getMutator().addValue(theElement, newString);
111                                        }
112
113                                }
114                        }
115
116                }
117
118        }
119
120}