001/*- 002 * #%L 003 * HAPI FHIR JPA - Search Parameters 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.searchparam.util; 021 022import ca.uhn.fhir.context.ConfigurationException; 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.context.RuntimeSearchParam; 025import ca.uhn.fhir.i18n.Msg; 026import ca.uhn.fhir.model.api.IQueryParameterAnd; 027import ca.uhn.fhir.model.api.IQueryParameterType; 028import ca.uhn.fhir.rest.api.QualifiedParamList; 029import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; 030import ca.uhn.fhir.rest.param.CompositeAndListParam; 031import ca.uhn.fhir.rest.param.DateAndListParam; 032import ca.uhn.fhir.rest.param.DateParam; 033import ca.uhn.fhir.rest.param.HasAndListParam; 034import ca.uhn.fhir.rest.param.HasParam; 035import ca.uhn.fhir.rest.param.NumberAndListParam; 036import ca.uhn.fhir.rest.param.NumberParam; 037import ca.uhn.fhir.rest.param.QuantityAndListParam; 038import ca.uhn.fhir.rest.param.QuantityParam; 039import ca.uhn.fhir.rest.param.ReferenceAndListParam; 040import ca.uhn.fhir.rest.param.ReferenceParam; 041import ca.uhn.fhir.rest.param.SpecialAndListParam; 042import ca.uhn.fhir.rest.param.SpecialParam; 043import ca.uhn.fhir.rest.param.StringAndListParam; 044import ca.uhn.fhir.rest.param.StringParam; 045import ca.uhn.fhir.rest.param.TokenAndListParam; 046import ca.uhn.fhir.rest.param.TokenParam; 047import ca.uhn.fhir.rest.param.UriAndListParam; 048import ca.uhn.fhir.rest.param.UriParam; 049import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; 050import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 051import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; 052import jakarta.annotation.Nonnull; 053 054import java.util.ArrayList; 055import java.util.Collections; 056import java.util.Comparator; 057import java.util.List; 058 059public enum JpaParamUtil { 060 ; 061 062 /** 063 * This is a utility method intended provided to help the JPA module. 064 */ 065 public static IQueryParameterAnd<?> parseQueryParams( 066 FhirContext theContext, 067 RestSearchParameterTypeEnum paramType, 068 String theUnqualifiedParamName, 069 List<QualifiedParamList> theParameters) { 070 QueryParameterAndBinder binder; 071 switch (paramType) { 072 case COMPOSITE: 073 throw new UnsupportedOperationException(Msg.code(496)); 074 case DATE: 075 binder = new QueryParameterAndBinder(DateAndListParam.class, Collections.emptyList()); 076 break; 077 case NUMBER: 078 binder = new QueryParameterAndBinder(NumberAndListParam.class, Collections.emptyList()); 079 break; 080 case QUANTITY: 081 binder = new QueryParameterAndBinder(QuantityAndListParam.class, Collections.emptyList()); 082 break; 083 case REFERENCE: 084 binder = new QueryParameterAndBinder(ReferenceAndListParam.class, Collections.emptyList()); 085 break; 086 case STRING: 087 binder = new QueryParameterAndBinder(StringAndListParam.class, Collections.emptyList()); 088 break; 089 case TOKEN: 090 binder = new QueryParameterAndBinder(TokenAndListParam.class, Collections.emptyList()); 091 break; 092 case URI: 093 binder = new QueryParameterAndBinder(UriAndListParam.class, Collections.emptyList()); 094 break; 095 case HAS: 096 binder = new QueryParameterAndBinder(HasAndListParam.class, Collections.emptyList()); 097 break; 098 case SPECIAL: 099 binder = new QueryParameterAndBinder(SpecialAndListParam.class, Collections.emptyList()); 100 break; 101 default: 102 throw new IllegalArgumentException(Msg.code(497) + "Parameter '" + theUnqualifiedParamName 103 + "' has type " + paramType + " which is currently not supported."); 104 } 105 106 return binder.parse(theContext, theUnqualifiedParamName, theParameters); 107 } 108 109 /** 110 * This is a utility method intended provided to help the JPA module. 111 */ 112 public static IQueryParameterAnd<?> parseQueryParams( 113 ISearchParamRegistry theSearchParamRegistry, 114 FhirContext theContext, 115 RuntimeSearchParam theParamDef, 116 String theUnqualifiedParamName, 117 List<QualifiedParamList> theParameters) { 118 119 RestSearchParameterTypeEnum paramType = theParamDef.getParamType(); 120 121 if (paramType == RestSearchParameterTypeEnum.COMPOSITE) { 122 123 List<RuntimeSearchParam> compositeList = resolveComponentParameters(theSearchParamRegistry, theParamDef); 124 125 if (compositeList.size() != 2) { 126 throw new ConfigurationException(Msg.code(498) + "Search parameter of type " + theUnqualifiedParamName 127 + " must have 2 composite types declared in parameter annotation, found " 128 + compositeList.size()); 129 } 130 131 RuntimeSearchParam left = compositeList.get(0); 132 RuntimeSearchParam right = compositeList.get(1); 133 134 @SuppressWarnings({"unchecked", "rawtypes"}) 135 CompositeAndListParam<IQueryParameterType, IQueryParameterType> cp = new CompositeAndListParam( 136 getCompositeBindingClass(left.getParamType(), left.getName()), 137 getCompositeBindingClass(right.getParamType(), right.getName())); 138 139 cp.setValuesAsQueryTokens(theContext, theUnqualifiedParamName, theParameters); 140 141 return cp; 142 } else { 143 return parseQueryParams(theContext, paramType, theUnqualifiedParamName, theParameters); 144 } 145 } 146 147 public static List<RuntimeSearchParam> resolveComponentParameters( 148 ISearchParamRegistry theSearchParamRegistry, RuntimeSearchParam theParamDef) { 149 List<RuntimeSearchParam> compositeList = 150 resolveCompositeComponentsDeclaredOrder(theSearchParamRegistry, theParamDef); 151 152 // todo mb why is this sorted? Is the param order flipped too during query-time? 153 compositeList.sort((Comparator.comparing(RuntimeSearchParam::getName))); 154 155 return compositeList; 156 } 157 158 @Nonnull 159 public static List<RuntimeSearchParam> resolveCompositeComponentsDeclaredOrder( 160 ISearchParamRegistry theSearchParamRegistry, RuntimeSearchParam theParamDef) { 161 List<RuntimeSearchParam> compositeList = new ArrayList<>(); 162 List<RuntimeSearchParam.Component> components = theParamDef.getComponents(); 163 for (RuntimeSearchParam.Component next : components) { 164 String url = next.getReference(); 165 RuntimeSearchParam componentParam = theSearchParamRegistry.getActiveSearchParamByUrl( 166 url, ISearchParamRegistry.SearchParamLookupContextEnum.ALL); 167 if (componentParam == null) { 168 throw new InternalErrorException(Msg.code(499) + "Can not find SearchParameter: " + url); 169 } 170 compositeList.add(componentParam); 171 } 172 return compositeList; 173 } 174 175 private static Class<?> getCompositeBindingClass( 176 RestSearchParameterTypeEnum paramType, String theUnqualifiedParamName) { 177 178 switch (paramType) { 179 case DATE: 180 return DateParam.class; 181 case NUMBER: 182 return NumberParam.class; 183 case QUANTITY: 184 return QuantityParam.class; 185 case REFERENCE: 186 return ReferenceParam.class; 187 case STRING: 188 return StringParam.class; 189 case TOKEN: 190 return TokenParam.class; 191 case URI: 192 return UriParam.class; 193 case HAS: 194 return HasParam.class; 195 case SPECIAL: 196 return SpecialParam.class; 197 198 case COMPOSITE: 199 default: 200 throw new IllegalArgumentException(Msg.code(500) + "Parameter '" + theUnqualifiedParamName 201 + "' has type " + paramType + " which is currently not supported."); 202 } 203 } 204}