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.i18n.Msg; 023import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 024import ca.uhn.fhir.model.api.IQueryParameterType; 025import ca.uhn.fhir.rest.param.QuantityParam; 026import ca.uhn.fhir.rest.param.ReferenceParam; 027import org.hl7.fhir.dstu3.model.Location; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029 030import java.util.Collection; 031import java.util.List; 032 033/** 034 * In DSTU3, the near-distance search parameter is separate from near. In this utility method, 035 * we search for near-distance search parameters and if we find any, remove them from the list 036 * of search parameters and store it in a dedicated field in {@link SearchParameterMap}. This is so that 037 * when the "near" search parameter is processed, we have access to this near-distance value. 038 * This requires at most one near-distance parameter. If more are found, we throw an {@link IllegalArgumentException}. 039 */ 040public class Dstu3DistanceHelper { 041 public static void setNearDistance(Class<? extends IBaseResource> theResourceType, SearchParameterMap theParams) { 042 if (theResourceType == Location.class && theParams.containsKey(Location.SP_NEAR_DISTANCE)) { 043 List<List<IQueryParameterType>> paramAndList = theParams.get(Location.SP_NEAR_DISTANCE); 044 QuantityParam quantityParam = getNearDistanceParam(paramAndList); 045 theParams.setNearDistanceParam(quantityParam); 046 047 // Need to remove near-distance or it we'll get a hashcode predicate for it 048 theParams.remove(Location.SP_NEAR_DISTANCE); 049 } else if (theParams.containsKey("location")) { 050 List<List<IQueryParameterType>> paramAndList = theParams.get("location"); 051 ReferenceParam referenceParam = getChainedLocationNearDistanceParam(paramAndList); 052 if (referenceParam != null) { 053 QuantityParam quantityParam = new QuantityParam(referenceParam.getValue()); 054 theParams.setNearDistanceParam(quantityParam); 055 } 056 } 057 } 058 059 private static ReferenceParam getChainedLocationNearDistanceParam(List<List<IQueryParameterType>> theParamAndList) { 060 ReferenceParam retval = null; 061 List<IQueryParameterType> andParamToRemove = null; 062 for (List<IQueryParameterType> paramOrList : theParamAndList) { 063 IQueryParameterType orParamToRemove = null; 064 for (IQueryParameterType param : paramOrList) { 065 if (param instanceof ReferenceParam) { 066 ReferenceParam referenceParam = (ReferenceParam) param; 067 if (Location.SP_NEAR_DISTANCE.equals(referenceParam.getChain())) { 068 if (retval != null) { 069 throw new IllegalArgumentException(Msg.code(494) + "Only one " + Location.SP_NEAR_DISTANCE 070 + " parameter may be present"); 071 } else { 072 retval = referenceParam; 073 orParamToRemove = param; 074 } 075 } 076 } 077 } 078 if (orParamToRemove != null) { 079 paramOrList.remove(orParamToRemove); 080 if (paramOrList.isEmpty()) { 081 andParamToRemove = paramOrList; 082 } 083 } 084 } 085 if (andParamToRemove != null) { 086 theParamAndList.remove(andParamToRemove); 087 } 088 return retval; 089 } 090 091 private static QuantityParam getNearDistanceParam(List<List<IQueryParameterType>> theParamAndList) { 092 long sum = theParamAndList.stream().mapToLong(Collection::size).sum(); 093 094 // No near-distance Param 095 if (sum == 0) { 096 return null; 097 // A single near-distance Param 098 } else if (sum == 1) { 099 return (QuantityParam) theParamAndList.get(0).get(0); 100 // Too many near-distance params 101 } else { 102 throw new IllegalArgumentException( 103 Msg.code(495) + "Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); 104 } 105 } 106}