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