001package ca.uhn.fhir.util;
002
003/*-
004 * #%L
005 * HAPI FHIR - Core Library
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.FhirContext;
025import ca.uhn.fhir.context.RuntimeResourceDefinition;
026import ca.uhn.fhir.context.RuntimeSearchParam;
027import org.apache.commons.lang3.Validate;
028import org.hl7.fhir.instance.model.api.IBase;
029import org.hl7.fhir.instance.model.api.IBaseResource;
030import org.hl7.fhir.instance.model.api.IPrimitiveType;
031
032import javax.annotation.Nullable;
033import java.util.ArrayList;
034import java.util.List;
035import java.util.Optional;
036
037public class SearchParameterUtil {
038
039        public static List<String> getBaseAsStrings(FhirContext theContext, IBaseResource theResource) {
040                Validate.notNull(theContext, "theContext must not be null");
041                Validate.notNull(theResource, "theResource must not be null");
042                RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
043
044                BaseRuntimeChildDefinition base = def.getChildByName("base");
045                List<IBase> baseValues = base.getAccessor().getValues(theResource);
046                List<String> retVal = new ArrayList<>();
047                for (IBase next : baseValues) {
048                        IPrimitiveType<?> nextPrimitive = (IPrimitiveType<?>) next;
049                        retVal.add(nextPrimitive.getValueAsString());
050                }
051
052                return retVal;
053        }
054
055        /**
056         * Given the resource type, fetch its patient-based search parameter name
057         * 1. Attempt to find one called 'patient'
058         * 2. If that fails, find one called 'subject'
059         * 3. If that fails, find find by Patient Compartment.
060         *    3.1 If that returns >1 result, throw an error
061         *    3.2 If that returns 1 result, return it
062         */
063        public static Optional<RuntimeSearchParam> getOnlyPatientSearchParamForResourceType(FhirContext theFhirContext, String theResourceType) {
064                RuntimeSearchParam myPatientSearchParam = null;
065                RuntimeResourceDefinition runtimeResourceDefinition = theFhirContext.getResourceDefinition(theResourceType);
066                myPatientSearchParam = runtimeResourceDefinition.getSearchParam("patient");
067                if (myPatientSearchParam == null) {
068                        myPatientSearchParam = runtimeResourceDefinition.getSearchParam("subject");
069                        if (myPatientSearchParam == null) {
070                                myPatientSearchParam = getOnlyPatientCompartmentRuntimeSearchParam(runtimeResourceDefinition);
071                        }
072                }
073                return Optional.ofNullable(myPatientSearchParam);
074        }
075
076
077        /**
078         * Search the resource definition for a compartment named 'patient' and return its related Search Parameter.
079         */
080        public static RuntimeSearchParam getOnlyPatientCompartmentRuntimeSearchParam(FhirContext theFhirContext, String theResourceType) {
081                RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResourceType);
082                return getOnlyPatientCompartmentRuntimeSearchParam(resourceDefinition);
083        }
084
085        public static RuntimeSearchParam getOnlyPatientCompartmentRuntimeSearchParam(RuntimeResourceDefinition runtimeResourceDefinition) {
086                RuntimeSearchParam patientSearchParam;
087                List<RuntimeSearchParam> searchParams = runtimeResourceDefinition.getSearchParamsForCompartmentName("Patient");
088                if (searchParams == null || searchParams.size() == 0) {
089                        String errorMessage = String.format("Resource type [%s] is not eligible for this type of export, as it contains no Patient compartment, and no `patient` or `subject` search parameter", runtimeResourceDefinition.getId());
090                        throw new IllegalArgumentException(errorMessage);
091                } else if (searchParams.size() == 1) {
092                        patientSearchParam = searchParams.get(0);
093                } else {
094                        String errorMessage = String.format("Resource type %s has more than one Search Param which references a patient compartment. We are unable to disambiguate which patient search parameter we should be searching by.", runtimeResourceDefinition.getId());
095                        throw new IllegalArgumentException(errorMessage);
096                }
097                return patientSearchParam;
098        }
099
100        public static List<RuntimeSearchParam> getAllPatientCompartmentRuntimeSearchParams(FhirContext theFhirContext, String theResourceType) {
101                RuntimeResourceDefinition runtimeResourceDefinition = theFhirContext.getResourceDefinition(theResourceType);
102                return getAllPatientCompartmentRuntimeSearchParams(runtimeResourceDefinition);
103
104        }
105
106        private static List<RuntimeSearchParam> getAllPatientCompartmentRuntimeSearchParams(RuntimeResourceDefinition theRuntimeResourceDefinition) {
107                List<RuntimeSearchParam> patient = theRuntimeResourceDefinition.getSearchParamsForCompartmentName("Patient");
108                return patient;
109        }
110
111
112        @Nullable
113        public static String getCode(FhirContext theContext, IBaseResource theResource) {
114                return getStringChild(theContext, theResource, "code");
115        }
116
117        @Nullable
118        public static String getExpression(FhirContext theFhirContext, IBaseResource theResource) {
119                return getStringChild(theFhirContext, theResource, "expression");
120        }
121
122        private static String getStringChild(FhirContext theFhirContext, IBaseResource theResource, String theChildName) {
123                Validate.notNull(theFhirContext, "theContext must not be null");
124                Validate.notNull(theResource, "theResource must not be null");
125                RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theResource);
126
127                BaseRuntimeChildDefinition base = def.getChildByName(theChildName);
128                return base
129                        .getAccessor()
130                        .getFirstValueOrNull(theResource)
131                        .map(t -> ((IPrimitiveType<?>) t))
132                        .map(t -> t.getValueAsString())
133                        .orElse(null);
134        }
135}