001package ca.uhn.fhir.util; 002 003/*- 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2021 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}