001/* 002 * #%L 003 * HAPI FHIR - Server Framework 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.rest.server.method; 021 022import ca.uhn.fhir.context.ConfigurationException; 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.context.FhirVersionEnum; 025import ca.uhn.fhir.i18n.Msg; 026import ca.uhn.fhir.rest.annotation.Sort; 027import ca.uhn.fhir.rest.api.Constants; 028import ca.uhn.fhir.rest.api.SortOrderEnum; 029import ca.uhn.fhir.rest.api.SortSpec; 030import ca.uhn.fhir.rest.api.server.RequestDetails; 031import ca.uhn.fhir.rest.param.ParameterUtil; 032import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 033import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 034 035import java.lang.reflect.Method; 036import java.util.Collection; 037import java.util.StringTokenizer; 038 039import static org.apache.commons.lang3.StringUtils.isNotBlank; 040 041public class SortParameter implements IParameter { 042 043 private FhirContext myContext; 044 045 public SortParameter(FhirContext theContext) { 046 myContext = theContext; 047 } 048 049 @Override 050 public void initializeTypes( 051 Method theMethod, 052 Class<? extends Collection<?>> theOuterCollectionType, 053 Class<? extends Collection<?>> theInnerCollectionType, 054 Class<?> theParameterType) { 055 if (theOuterCollectionType != null || theInnerCollectionType != null) { 056 throw new ConfigurationException(Msg.code(443) + "Method '" + theMethod.getName() + "' in type '" 057 + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() 058 + " but can not be of collection type"); 059 } 060 if (!theParameterType.equals(SortSpec.class)) { 061 throw new ConfigurationException(Msg.code(444) + "Method '" + theMethod.getName() + "' in type '" 062 + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() 063 + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName()); 064 } 065 } 066 067 @Override 068 public Object translateQueryParametersIntoServerArgument( 069 RequestDetails theRequest, BaseMethodBinding theMethodBinding) 070 throws InternalErrorException, InvalidRequestException { 071 if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) { 072 if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) { 073 if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) { 074 return null; 075 } 076 } 077 } 078 079 SortSpec outerSpec = null; 080 SortSpec innerSpec = null; 081 for (String nextParamName : theRequest.getParameters().keySet()) { 082 SortOrderEnum order; 083 if (Constants.PARAM_SORT.equals(nextParamName)) { 084 order = null; 085 } else if (Constants.PARAM_SORT_ASC.equals(nextParamName)) { 086 order = SortOrderEnum.ASC; 087 } else if (Constants.PARAM_SORT_DESC.equals(nextParamName)) { 088 order = SortOrderEnum.DESC; 089 } else { 090 continue; 091 } 092 093 String[] values = theRequest.getParameters().get(nextParamName); 094 if (values != null) { 095 096 for (String nextValue : values) { 097 098 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) && order == null) { 099 StringTokenizer tok = new StringTokenizer(nextValue, ","); 100 while (tok.hasMoreTokens()) { 101 String next = tok.nextToken(); 102 if (isNotBlank(next) && !next.equals("-")) { 103 order = SortOrderEnum.ASC; 104 if (next.startsWith("-")) { 105 order = SortOrderEnum.DESC; 106 next = next.substring(1); 107 } 108 109 SortSpec spec = new SortSpec(); 110 spec.setOrder(order); 111 spec.setParamName(next); 112 if (innerSpec == null) { 113 outerSpec = spec; 114 innerSpec = spec; 115 } else { 116 innerSpec.setChain(spec); 117 innerSpec = spec; 118 } 119 } 120 } 121 122 } else { 123 124 if (isNotBlank(nextValue)) { 125 SortSpec spec = new SortSpec(); 126 spec.setOrder(order); 127 spec.setParamName(nextValue); 128 if (innerSpec == null) { 129 outerSpec = spec; 130 innerSpec = spec; 131 } else { 132 innerSpec.setChain(spec); 133 innerSpec = spec; 134 } 135 } 136 } 137 } 138 } 139 } 140 141 return outerSpec; 142 } 143 144 public static String createSortStringDstu3(SortSpec ss) { 145 StringBuilder val = new StringBuilder(); 146 while (ss != null) { 147 148 if (isNotBlank(ss.getParamName())) { 149 if (val.length() > 0) { 150 val.append(','); 151 } 152 if (ss.getOrder() == SortOrderEnum.DESC) { 153 val.append('-'); 154 } 155 val.append(ParameterUtil.escape(ss.getParamName())); 156 } 157 158 ss = ss.getChain(); 159 } 160 161 String string = val.toString(); 162 return string; 163 } 164}