EMMA Coverage Report (generated Wed Aug 10 12:30:04 EDT 2011)
[all classes][org.jclouds.rest.internal]

COVERAGE SUMMARY FOR SOURCE FILE [RestAnnotationProcessor.java]

nameclass, %method, %block, %line, %
RestAnnotationProcessor.java100% (16/16)97%  (77/79)85%  (3010/3561)86%  (526.7/616)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class RestAnnotationProcessor$MethodKey100% (1/1)100% (3/3)81%  (112/138)70%  (23.8/34)
equals (Object): boolean 100% (1/1)63%  (38/60)50%  (10/20)
hashCode (): int 100% (1/1)90%  (35/39)97%  (5.8/6)
RestAnnotationProcessor$MethodKey (Method): void 100% (1/1)100% (39/39)100% (8/8)
     
class RestAnnotationProcessor100% (1/1)96%  (51/53)84%  (2744/3269)86%  (481.9/561)
indexWithOnlyOneAnnotation (Method, String, Map): Map 0%   (0/1)0%   (0/31)0%   (0/4)
replaceQuery (URI, String, Comparator): URI 0%   (0/1)0%   (0/9)0%   (0/1)
findOptionsIn (Method, Object []): HttpRequestOptions 100% (1/1)39%  (32/82)32%  (4.8/15)
getHttpMethodOrConstantOrThrowException (Method): String 100% (1/1)52%  (14/27)75%  (3/4)
getMapPayloadBinderOrNull (Method, Object []): MapBinder 100% (1/1)56%  (59/106)56%  (11.8/21)
addProducesIfPresentOnTypeOrMethod (Multimap, Method): void 100% (1/1)63%  (22/35)71%  (5/7)
decorateRequest (GeneratedHttpRequest): GeneratedHttpRequest 100% (1/1)64%  (132/206)61%  (17/28)
addForm (Multimap, FormParams, Collection): void 100% (1/1)65%  (30/46)67%  (4/6)
addMatrix (Multimap, MatrixParams, Collection): void 100% (1/1)65%  (30/46)67%  (4/6)
createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation (Injector, Me... 100% (1/1)67%  (12/18)75%  (3/4)
getAcceptHeadersOrNull (Method): List 100% (1/1)69%  (22/32)75%  (6/8)
getMatrixParamKeyValues (Method, Object []): Multimap 100% (1/1)70%  (84/120)76%  (14.5/19)
getEndpointInParametersOrNull (Method, Object [], Injector): URI 100% (1/1)72%  (102/141)79%  (14.9/19)
createResponseParser (ParseSax$Factory, Injector, Method, HttpRequest): Function 100% (1/1)72%  (21/29)86%  (6/7)
getSaxResponseParserClassOrNull (Method): Class 100% (1/1)75%  (9/12)75%  (3/4)
getFormParamKeyValues (Method, Object []): Multimap 100% (1/1)77%  (92/120)82%  (15.5/19)
getQueryParamKeyValues (Method, Object []): Multimap 100% (1/1)77%  (92/120)82%  (15.5/19)
addMapPayload (Map, PayloadParams, Collection): void 100% (1/1)77%  (30/39)80%  (4/5)
buildPostParams (Method, Object []): Map 100% (1/1)77%  (75/97)80%  (11.9/15)
getJsonParserKeyForMethodAnType (Method, Type): Key 100% (1/1)85%  (93/110)93%  (14/15)
findHttpRequestInArgs (Object []): HttpRequest 100% (1/1)92%  (24/26)83%  (5/6)
createRequest (Method, Object []): GeneratedHttpRequest 100% (1/1)93%  (494/534)93%  (87/94)
findPayloadInArgs (Object []): Payload 100% (1/1)95%  (37/39)88%  (7/8)
getParserOrThrowException (Method): Key 100% (1/1)95%  (115/121)89%  (16/18)
<static initializer> 100% (1/1)100% (83/83)100% (23/23)
RestAnnotationProcessor (Injector, ConcurrentMap, String, ParseSax$Factory, H... 100% (1/1)100% (53/53)100% (14/14)
access$100 (): Class 100% (1/1)100% (2/2)100% (1/1)
addConsumesIfPresentOnTypeOrMethod (Multimap, Method): void 100% (1/1)100% (12/12)100% (4/4)
addFormParams (Collection, Method, Object []): Multimap 100% (1/1)100% (59/59)100% (10/10)
addHeader (Multimap, Headers, Collection): void 100% (1/1)100% (27/27)100% (5/5)
addHeaderIfAnnotationPresentOnMethod (Multimap, Method, Collection): void 100% (1/1)100% (31/31)100% (7/7)
addMatrixParams (Collection, Method, Object []): Multimap 100% (1/1)100% (59/59)100% (10/10)
addPathAndGetTokens (Class, Method, Object [], UriBuilder): Multimap 100% (1/1)100% (25/25)100% (5/5)
addQuery (Multimap, QueryParams, Collection): void 100% (1/1)100% (46/46)100% (6/6)
addQueryParams (Collection, Method, Object []): Multimap 100% (1/1)100% (59/59)100% (10/10)
buildHeaders (Collection, Method, Object []): Multimap 100% (1/1)100% (66/66)100% (12/12)
createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation (Method): Fun... 100% (1/1)100% (5/5)100% (1/1)
createMethodToIndexOfParamToAnnotation (Class): Map 100% (1/1)100% (9/9)100% (1/1)
createResponseParser (Method, HttpRequest): Function 100% (1/1)100% (8/8)100% (1/1)
encodeValues (Multimap, char []): Multimap 100% (1/1)100% (26/26)100% (4/4)
filterOutContentHeaders (Multimap): Multimap 100% (1/1)100% (30/30)100% (5/5)
getDelegateOrNull (Method): Method 100% (1/1)100% (8/8)100% (1/1)
getEndpointFor (Method, Object [], Injector): URI 100% (1/1)100% (51/51)100% (9/9)
getFiltersIfAnnotated (Method): List 100% (1/1)100% (111/111)100% (14/14)
getHttpMethods (Method): Set 100% (1/1)100% (42/42)100% (7/7)
getJsonParserKeyForMethod (Method): Key 100% (1/1)100% (7/7)100% (2/2)
getParts (Method, Object [], Iterable): List 100% (1/1)100% (79/79)100% (14/14)
getPathParamKeyValues (Method, Object []): Multimap 100% (1/1)100% (120/120)100% (19/19)
getReturnTypeForMethod (Method): Type 100% (1/1)100% (40/40)100% (10/10)
indexWithAtLeastOneAnnotation (Method, Map): Map 100% (1/1)100% (11/11)100% (2/2)
replaceQuery (Provider, URI, String, Comparator, char []): URI 100% (1/1)100% (19/19)100% (3/3)
setCaller (ClassMethodArgs): void 100% (1/1)100% (22/22)100% (6/6)
shouldAddHostHeader (Method): boolean 100% (1/1)100% (13/13)100% (3/3)
     
class RestAnnotationProcessor$1100% (1/1)100% (2/2)100% (17/17)100% (2/2)
RestAnnotationProcessor$1 (Class): void 100% (1/1)100% (6/6)100% (1/1)
apply (Method): Map 100% (1/1)100% (11/11)100% (1/1)
     
class RestAnnotationProcessor$10100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$10 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$11100% (1/1)100% (2/2)100% (11/11)100% (2/2)
RestAnnotationProcessor$11 (): void 100% (1/1)100% (3/3)100% (1/1)
apply (Set): boolean 100% (1/1)100% (8/8)100% (1/1)
     
class RestAnnotationProcessor$12100% (1/1)100% (2/2)100% (11/11)100% (2/2)
RestAnnotationProcessor$12 (): void 100% (1/1)100% (3/3)100% (1/1)
apply (Set): boolean 100% (1/1)100% (8/8)100% (1/1)
     
class RestAnnotationProcessor$2100% (1/1)100% (2/2)100% (11/11)100% (2/2)
RestAnnotationProcessor$2 (): void 100% (1/1)100% (3/3)100% (1/1)
apply (Map$Entry): Part 100% (1/1)100% (8/8)100% (1/1)
     
class RestAnnotationProcessor$3100% (1/1)100% (2/2)100% (35/35)100% (7/7)
RestAnnotationProcessor$3 (): void 100% (1/1)100% (3/3)100% (1/1)
apply (Method): Set 100% (1/1)100% (32/32)100% (6/6)
     
class RestAnnotationProcessor$4100% (1/1)100% (2/2)100% (12/12)100% (2/2)
RestAnnotationProcessor$4 (Object []): void 100% (1/1)100% (6/6)100% (1/1)
apply (Integer): Object 100% (1/1)100% (6/6)100% (1/1)
     
class RestAnnotationProcessor$5100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$5 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$6100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$6 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$7100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$7 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$8100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$8 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$9100% (1/1)100% (1/1)100% (3/3)100% (1/1)
RestAnnotationProcessor$9 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class RestAnnotationProcessor$GetAnnotationsForMethodParameterIndex100% (1/1)100% (3/3)100% (26/26)100% (6/6)
RestAnnotationProcessor$GetAnnotationsForMethodParameterIndex (Method, Class)... 100% (1/1)100% (9/9)100% (4/4)
access$000 (RestAnnotationProcessor$GetAnnotationsForMethodParameterIndex): C... 100% (1/1)100% (3/3)100% (1/1)
apply (Integer): Set 100% (1/1)100% (14/14)100% (1/1)
     
class RestAnnotationProcessor$GetAnnotationsForMethodParameterIndex$1100% (1/1)100% (2/2)100% (13/13)100% (2/2)
RestAnnotationProcessor$GetAnnotationsForMethodParameterIndex$1 (RestAnnotati... 100% (1/1)100% (6/6)100% (1/1)
apply (Annotation): boolean 100% (1/1)100% (7/7)100% (1/1)

1/**
2 *
3 * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
4 *
5 * ====================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 * ====================================================================
18 */
19package org.jclouds.rest.internal;
20 
21import static com.google.common.base.Preconditions.checkArgument;
22import static com.google.common.collect.Collections2.filter;
23import static com.google.common.collect.Iterables.concat;
24import static com.google.common.collect.Iterables.get;
25import static com.google.common.collect.Iterables.transform;
26import static com.google.common.collect.Lists.newLinkedList;
27import static com.google.common.collect.Maps.filterValues;
28import static com.google.common.collect.Maps.newHashMap;
29import static com.google.common.collect.Sets.newTreeSet;
30import static java.util.Arrays.asList;
31import static javax.ws.rs.core.HttpHeaders.ACCEPT;
32import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
33import static javax.ws.rs.core.HttpHeaders.HOST;
34import static org.jclouds.io.Payloads.newPayload;
35 
36import java.io.InputStream;
37import java.lang.annotation.Annotation;
38import java.lang.reflect.Array;
39import java.lang.reflect.Method;
40import java.lang.reflect.ParameterizedType;
41import java.lang.reflect.Type;
42import java.lang.reflect.WildcardType;
43import java.net.URI;
44import java.util.Collection;
45import java.util.Collections;
46import java.util.Comparator;
47import java.util.List;
48import java.util.Map;
49import java.util.Set;
50import java.util.SortedSet;
51import java.util.Map.Entry;
52import java.util.concurrent.ConcurrentMap;
53 
54import javax.annotation.Nullable;
55import javax.annotation.Resource;
56import javax.inject.Named;
57import javax.inject.Provider;
58import javax.ws.rs.Consumes;
59import javax.ws.rs.FormParam;
60import javax.ws.rs.HeaderParam;
61import javax.ws.rs.HttpMethod;
62import javax.ws.rs.MatrixParam;
63import javax.ws.rs.Path;
64import javax.ws.rs.PathParam;
65import javax.ws.rs.Produces;
66import javax.ws.rs.QueryParam;
67import javax.ws.rs.core.MediaType;
68import javax.ws.rs.core.UriBuilder;
69import javax.ws.rs.core.UriBuilderException;
70 
71import org.jclouds.Constants;
72import org.jclouds.functions.IdentityFunction;
73import org.jclouds.http.HttpRequest;
74import org.jclouds.http.HttpRequestFilter;
75import org.jclouds.http.HttpResponse;
76import org.jclouds.http.HttpUtils;
77import org.jclouds.http.functions.ParseJson;
78import org.jclouds.http.functions.ParseSax;
79import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
80import org.jclouds.http.functions.ReleasePayloadAndReturn;
81import org.jclouds.http.functions.ReturnInputStream;
82import org.jclouds.http.functions.ReturnStringIf2xx;
83import org.jclouds.http.functions.ReturnTrueIf2xx;
84import org.jclouds.http.functions.UnwrapOnlyJsonValue;
85import org.jclouds.http.functions.UnwrapOnlyJsonValueInSet;
86import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
87import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
88import org.jclouds.http.functions.ParseSax.HandlerWithResult;
89import org.jclouds.http.options.HttpRequestOptions;
90import org.jclouds.http.utils.ModifyRequest;
91import org.jclouds.internal.ClassMethodArgs;
92import org.jclouds.io.ContentMetadata;
93import org.jclouds.io.Payload;
94import org.jclouds.io.PayloadEnclosing;
95import org.jclouds.io.Payloads;
96import org.jclouds.io.payloads.MultipartForm;
97import org.jclouds.io.payloads.Part;
98import org.jclouds.io.payloads.Part.PartOptions;
99import org.jclouds.logging.Logger;
100import org.jclouds.rest.Binder;
101import org.jclouds.rest.InputParamValidator;
102import org.jclouds.rest.InvocationContext;
103import org.jclouds.rest.annotations.BinderParam;
104import org.jclouds.rest.annotations.Endpoint;
105import org.jclouds.rest.annotations.EndpointParam;
106import org.jclouds.rest.annotations.ExceptionParser;
107import org.jclouds.rest.annotations.FormParams;
108import org.jclouds.rest.annotations.Headers;
109import org.jclouds.rest.annotations.MapBinder;
110import org.jclouds.rest.annotations.MatrixParams;
111import org.jclouds.rest.annotations.OverrideRequestFilters;
112import org.jclouds.rest.annotations.ParamParser;
113import org.jclouds.rest.annotations.PartParam;
114import org.jclouds.rest.annotations.PayloadParam;
115import org.jclouds.rest.annotations.PayloadParams;
116import org.jclouds.rest.annotations.QueryParams;
117import org.jclouds.rest.annotations.RequestFilters;
118import org.jclouds.rest.annotations.ResponseParser;
119import org.jclouds.rest.annotations.SkipEncoding;
120import org.jclouds.rest.annotations.Unwrap;
121import org.jclouds.rest.annotations.VirtualHost;
122import org.jclouds.rest.annotations.WrapWith;
123import org.jclouds.rest.annotations.XMLResponseParser;
124import org.jclouds.rest.binders.BindMapToStringPayload;
125import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
126import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
127import org.jclouds.util.Maps2;
128import org.jclouds.util.Strings2;
129 
130import com.google.common.annotations.VisibleForTesting;
131import com.google.common.base.Function;
132import com.google.common.base.Predicate;
133import com.google.common.base.Predicates;
134import com.google.common.collect.ImmutableList;
135import com.google.common.collect.ImmutableMultimap;
136import com.google.common.collect.ImmutableSet;
137import com.google.common.collect.Iterables;
138import com.google.common.collect.LinkedHashMultimap;
139import com.google.common.collect.LinkedListMultimap;
140import com.google.common.collect.Lists;
141import com.google.common.collect.MapMaker;
142import com.google.common.collect.Multimap;
143import com.google.common.collect.ImmutableSet.Builder;
144import com.google.common.util.concurrent.ListenableFuture;
145import com.google.inject.Inject;
146import com.google.inject.Injector;
147import com.google.inject.Key;
148import com.google.inject.TypeLiteral;
149import com.google.inject.util.Types;
150 
151/**
152 * Creates http methods based on annotations on a class or interface.
153 * 
154 * @author Adrian Cole
155 */
156public class RestAnnotationProcessor<T> {
157 
158   @Resource
159   protected Logger logger = Logger.NULL;
160 
161   private final Class<T> declaring;
162 
163   // TODO replace with Table object
164   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
165   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class);
166   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
167   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class);
168   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class);
169   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class);
170   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class);
171   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class);
172   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class);
173   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class);
174   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class);
175   static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class);
176   static final Map<MethodKey, Method> delegationMap = newHashMap();
177 
178   static Map<Method, Map<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation(
179            final Class<? extends Annotation> annotation) {
180      return new MapMaker().makeComputingMap(new Function<Method, Map<Integer, Set<Annotation>>>() {
181         public Map<Integer, Set<Annotation>> apply(Method method) {
182            return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation));
183         }
184      });
185   }
186 
187   static class GetAnnotationsForMethodParameterIndex implements Function<Integer, Set<Annotation>> {
188      private final Method method;
189      private final Class<?> clazz;
190 
191      protected GetAnnotationsForMethodParameterIndex(Method method, Class<?> clazz) {
192         this.method = method;
193         this.clazz = clazz;
194      }
195 
196      public Set<Annotation> apply(final Integer index) {
197         return ImmutableSet.<Annotation> copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]),
198                  new Predicate<Annotation>() {
199                     public boolean apply(Annotation input) {
200                        return input.annotationType().equals(clazz);
201                     }
202                  }));
203      }
204 
205   }
206 
207   private static final Class<? extends HttpRequestOptions[]> optionsVarArgsClass = new HttpRequestOptions[] {}
208            .getClass();
209 
210   private static final Function<? super Entry<String, String>, ? extends Part> ENTRY_TO_PART = new Function<Entry<String, String>, Part>() {
211 
212      @Override
213      public Part apply(Entry<String, String> from) {
214         return Part.create(from.getKey(), from.getValue());
215      }
216 
217   };
218 
219   static final Map<Method, Set<Integer>> methodToIndexesOfOptions = new MapMaker()
220            .makeComputingMap(new Function<Method, Set<Integer>>() {
221               public Set<Integer> apply(Method method) {
222                  Builder<Integer> toReturn = ImmutableSet.<Integer> builder();
223                  for (int index = 0; index < method.getParameterTypes().length; index++) {
224                     Class<?> type = method.getParameterTypes()[index];
225                     if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type))
226                        toReturn.add(index);
227                  }
228                  return toReturn.build();
229               }
230            });
231 
232   private final ParseSax.Factory parserFactory;
233   private final HttpUtils utils;
234   private final Provider<UriBuilder> uriBuilderProvider;
235   private final ConcurrentMap<Class<?>, Boolean> seedAnnotationCache;
236   private final String apiVersion;
237   private char[] skips;
238 
239   @Inject
240   private InputParamValidator inputParamValidator;
241 
242   @VisibleForTesting
243   Function<HttpResponse, ?> createResponseParser(Method method, HttpRequest request) {
244      return createResponseParser(parserFactory, injector, method, request);
245   }
246 
247   @VisibleForTesting
248   public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
249            Method method, HttpRequest request) {
250      Function<HttpResponse, ?> transformer;
251      Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
252      if (handler != null) {
253         transformer = parserFactory.create(injector.getInstance(handler));
254      } else {
255         transformer = injector.getInstance(getParserOrThrowException(method));
256      }
257      if (transformer instanceof InvocationContext<?>) {
258         ((InvocationContext<?>) transformer).setContext(request);
259      }
260      return transformer;
261   }
262 
263   @VisibleForTesting
264   Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(Method method) {
265      return createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(injector, method);
266   }
267 
268   @VisibleForTesting
269   public static Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(
270            Injector injector, Method method) {
271      ExceptionParser annotation = method.getAnnotation(ExceptionParser.class);
272      if (annotation != null) {
273         return injector.getInstance(annotation.value());
274      }
275      return injector.getInstance(MapHttp4xxCodesToExceptions.class);
276   }
277 
278   @SuppressWarnings("unchecked")
279   @Inject
280   public RestAnnotationProcessor(Injector injector, ConcurrentMap<Class<?>, Boolean> seedAnnotationCache,
281            @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils,
282            TypeLiteral<T> typeLiteral) {
283      this.declaring = (Class<T>) typeLiteral.getRawType();
284      this.injector = injector;
285      this.parserFactory = parserFactory;
286      this.utils = utils;
287      this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
288      this.seedAnnotationCache = seedAnnotationCache;
289      seedAnnotationCache.get(declaring);
290      if (declaring.isAnnotationPresent(SkipEncoding.class)) {
291         skips = declaring.getAnnotation(SkipEncoding.class).value();
292      } else {
293         skips = new char[] {};
294      }
295      this.apiVersion = apiVersion;
296   }
297 
298   public Method getDelegateOrNull(Method in) {
299      return delegationMap.get(new MethodKey(in));
300   }
301 
302   public static class MethodKey {
303 
304      @Override
305      public int hashCode() {
306         final int prime = 31;
307         int result = 1;
308         result = prime * result + ((declaringPackage == null) ? 0 : declaringPackage.hashCode());
309         result = prime * result + ((name == null) ? 0 : name.hashCode());
310         result = prime * result + parametersTypeHashCode;
311         return result;
312      }
313 
314      @Override
315      public boolean equals(Object obj) {
316         if (this == obj)
317            return true;
318         if (obj == null)
319            return false;
320         if (getClass() != obj.getClass())
321            return false;
322         MethodKey other = (MethodKey) obj;
323         if (declaringPackage == null) {
324            if (other.declaringPackage != null)
325               return false;
326         } else if (!declaringPackage.equals(other.declaringPackage))
327            return false;
328         if (name == null) {
329            if (other.name != null)
330               return false;
331         } else if (!name.equals(other.name))
332            return false;
333         if (parametersTypeHashCode != other.parametersTypeHashCode)
334            return false;
335         return true;
336      }
337 
338      private final String name;
339      private final int parametersTypeHashCode;
340      private final Package declaringPackage;
341 
342      public MethodKey(Method method) {
343         this.name = method.getName();
344         this.declaringPackage = method.getDeclaringClass().getPackage();
345         int parametersTypeHashCode = 0;
346         for (Class<?> param: method.getParameterTypes())
347            parametersTypeHashCode +=param.hashCode();
348         this.parametersTypeHashCode = parametersTypeHashCode;
349      }
350 
351   }
352 
353   final Injector injector;
354 
355   private ClassMethodArgs caller;
356   private URI callerEndpoint;
357 
358   public void setCaller(ClassMethodArgs caller) {
359      seedAnnotationCache.get(caller.getMethod().getDeclaringClass());
360      this.caller = caller;
361      try {
362         callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector);
363      } catch (IllegalStateException e) {
364      }
365   }
366 
367   public GeneratedHttpRequest<T> createRequest(Method method, Object... args) {
368      inputParamValidator.validateMethodParametersOrThrow(method, args);
369      ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
370               : null;
371 
372      URI endpoint = callerEndpoint;
373      try {
374         if (endpoint == null) {
375            endpoint = getEndpointFor(method, args, injector);
376            logger.trace("using endpoint %s for %s", endpoint, cma);
377         } else {
378            logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma);
379         }
380      } catch (IllegalStateException e) {
381         logger.trace("looking up default endpoint for %s", cma);
382         endpoint = injector.getInstance(Key.get(URI.class, org.jclouds.location.Provider.class));
383         logger.trace("using default endpoint %s for %s", endpoint, cma);
384      }
385      GeneratedHttpRequest.Builder<T> requestBuilder;
386      HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
387      if (r != null) {
388         requestBuilder = GeneratedHttpRequest.Builder.<T> from(r);
389         endpoint = r.getEndpoint();
390      } else {
391         requestBuilder = GeneratedHttpRequest.<T> builder();
392         requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
393      }
394 
395      requestBuilder.declaring(declaring).javaMethod(method).args(args).skips(skips);
396      requestBuilder.filters(getFiltersIfAnnotated(method));
397 
398      UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
399 
400      Multimap<String, String> tokenValues = LinkedHashMultimap.create();
401 
402      tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
403 
404      tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
405 
406      Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
407      Multimap<String, String> queryParams = addQueryParams(tokenValues.entries(), method, args);
408      Multimap<String, String> matrixParams = addMatrixParams(tokenValues.entries(), method, args);
409      Multimap<String, String> headers = buildHeaders(tokenValues.entries(), method, args);
410      if (r != null)
411         headers.putAll(r.getHeaders());
412 
413      if (shouldAddHostHeader(method)) {
414         StringBuilder hostHeader = new StringBuilder(endpoint.getHost());
415         if (endpoint.getPort() != -1)
416            hostHeader.append(":").append(endpoint.getPort());
417         headers.put(HOST, hostHeader.toString());
418      }
419 
420      Payload payload = null;
421      HttpRequestOptions options = findOptionsIn(method, args);
422      if (options != null) {
423         injector.injectMembers(options);// TODO test case
424         for (Entry<String, String> header : options.buildRequestHeaders().entries()) {
425            headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries()));
426         }
427         for (Entry<String, String> matrix : options.buildMatrixParameters().entries()) {
428            matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries()));
429         }
430         for (Entry<String, String> query : options.buildQueryParameters().entries()) {
431            queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries()));
432         }
433         for (Entry<String, String> form : options.buildFormParameters().entries()) {
434            formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries()));
435         }
436 
437         String pathSuffix = options.buildPathSuffix();
438         if (pathSuffix != null) {
439            builder.path(pathSuffix);
440         }
441         String stringPayload = options.buildStringPayload();
442         if (stringPayload != null)
443            payload = Payloads.newStringPayload(stringPayload);
444      }
445 
446      if (matrixParams.size() > 0) {
447         for (String key : matrixParams.keySet())
448            builder.matrixParam(key, Lists.newArrayList(matrixParams.get(key)).toArray());
449      }
450 
451      if (queryParams.size() > 0) {
452         builder.replaceQuery(ModifyRequest.makeQueryLine(queryParams, null, skips));
453      }
454 
455      requestBuilder.headers(filterOutContentHeaders(headers));
456 
457      try {
458         requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues)));
459      } catch (IllegalArgumentException e) {
460         throw new IllegalStateException(e);
461      } catch (UriBuilderException e) {
462         throw new IllegalStateException(e);
463      }
464 
465      if (payload == null)
466         payload = findPayloadInArgs(args);
467      List<? extends Part> parts = getParts(method, args, concat(tokenValues.entries(), formParams.entries()));
468      if (parts.size() > 0) {
469         if (formParams.size() > 0) {
470            parts = newLinkedList(concat(transform(formParams.entries(), ENTRY_TO_PART), parts));
471         }
472         payload = new MultipartForm(BOUNDARY, parts);
473      } else if (formParams.size() > 0) {
474         payload = Payloads.newUrlEncodedFormPayload(formParams, skips);
475      } else if (headers.containsKey(CONTENT_TYPE)) {
476         if (payload == null)
477            payload = Payloads.newByteArrayPayload(new byte[] {});
478         payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0));
479      }
480      if (payload != null) {
481         requestBuilder.payload(payload);
482      }
483      GeneratedHttpRequest<T> request = requestBuilder.build();
484 
485      org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args);
486      if (mapBinder != null) {
487         Map<String, String> mapParams = buildPostParams(method, args);
488         if (method.isAnnotationPresent(PayloadParams.class)) {
489            PayloadParams params = method.getAnnotation(PayloadParams.class);
490            addMapPayload(mapParams, params, headers.entries());
491         }
492         request = mapBinder.bindToRequest(request, mapParams);
493      } else {
494         request = decorateRequest(request);
495      }
496 
497      if (request.getPayload() != null)
498         request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers);
499      utils.checkRequestHasRequiredProperties(request);
500      return request;
501   }
502 
503   public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
504      // TODO make a filter like {@link Maps.filterKeys} instead of this
505      ImmutableMultimap.Builder<String, String> headersBuilder = ImmutableMultimap.builder();
506      // http message usually comes in as a null key header, let's filter it out.
507      for (String header : Iterables.filter(headers.keySet(), Predicates.notNull())) {
508         if (!ContentMetadata.HTTP_HEADERS.contains(header)) {
509            headersBuilder.putAll(header, headers.get(header));
510         }
511      }
512      return headersBuilder.build();
513   }
514 
515   public static final String BOUNDARY = "--JCLOUDS--";
516 
517   private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) {
518      if (clazz.isAnnotationPresent(Path.class))
519         builder.path(clazz);
520      if (method.isAnnotationPresent(Path.class))
521         builder.path(method);
522      return encodeValues(getPathParamKeyValues(method, args), skips);
523   }
524 
525   public URI replaceQuery(URI in, String newQuery, @Nullable Comparator<Entry<String, String>> sorter) {
526      return replaceQuery(uriBuilderProvider, in, newQuery, sorter, skips);
527   }
528 
529   public static URI replaceQuery(Provider<UriBuilder> uriBuilderProvider, URI in, String newQuery,
530            @Nullable Comparator<Entry<String, String>> sorter, char... skips) {
531      UriBuilder builder = uriBuilderProvider.get().uri(in);
532      builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips));
533      return builder.build();
534   }
535 
536   private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
537            Object... args) {
538      Multimap<String, String> matrixMap = LinkedListMultimap.create();
539      if (declaring.isAnnotationPresent(MatrixParams.class)) {
540         MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
541         addMatrix(matrixMap, matrix, tokenValues);
542      }
543 
544      if (method.isAnnotationPresent(MatrixParams.class)) {
545         MatrixParams matrix = method.getAnnotation(MatrixParams.class);
546         addMatrix(matrixMap, matrix, tokenValues);
547      }
548 
549      for (Entry<String, String> matrix : getMatrixParamKeyValues(method, args).entries()) {
550         matrixMap.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues));
551      }
552      return matrixMap;
553   }
554 
555   private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
556            Object... args) {
557      Multimap<String, String> formMap = LinkedListMultimap.create();
558      if (declaring.isAnnotationPresent(FormParams.class)) {
559         FormParams form = declaring.getAnnotation(FormParams.class);
560         addForm(formMap, form, tokenValues);
561      }
562 
563      if (method.isAnnotationPresent(FormParams.class)) {
564         FormParams form = method.getAnnotation(FormParams.class);
565         addForm(formMap, form, tokenValues);
566      }
567 
568      for (Entry<String, String> form : getFormParamKeyValues(method, args).entries()) {
569         formMap.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues));
570      }
571      return formMap;
572   }
573 
574   private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
575            Object... args) {
576      Multimap<String, String> queryMap = LinkedListMultimap.create();
577      if (declaring.isAnnotationPresent(QueryParams.class)) {
578         QueryParams query = declaring.getAnnotation(QueryParams.class);
579         addQuery(queryMap, query, tokenValues);
580      }
581 
582      if (method.isAnnotationPresent(QueryParams.class)) {
583         QueryParams query = method.getAnnotation(QueryParams.class);
584         addQuery(queryMap, query, tokenValues);
585      }
586 
587      for (Entry<String, String> query : getQueryParamKeyValues(method, args).entries()) {
588         queryMap.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues));
589      }
590      return queryMap;
591   }
592 
593   private void addForm(Multimap<String, String> formParams, FormParams form,
594            Collection<Entry<String, String>> tokenValues) {
595      for (int i = 0; i < form.keys().length; i++) {
596         if (form.values()[i].equals(FormParams.NULL)) {
597            formParams.removeAll(form.keys()[i]);
598            formParams.put(form.keys()[i], null);
599         } else {
600            formParams.put(form.keys()[i], Strings2.replaceTokens(form.values()[i], tokenValues));
601         }
602      }
603   }
604 
605   private void addQuery(Multimap<String, String> queryParams, QueryParams query,
606            Collection<Entry<String, String>> tokenValues) {
607      for (int i = 0; i < query.keys().length; i++) {
608         if (query.values()[i].equals(QueryParams.NULL)) {
609            queryParams.removeAll(query.keys()[i]);
610            queryParams.put(query.keys()[i], null);
611         } else {
612            queryParams.put(query.keys()[i], Strings2.replaceTokens(query.values()[i], tokenValues));
613         }
614      }
615   }
616 
617   private void addMatrix(Multimap<String, String> matrixParams, MatrixParams matrix,
618            Collection<Entry<String, String>> tokenValues) {
619      for (int i = 0; i < matrix.keys().length; i++) {
620         if (matrix.values()[i].equals(MatrixParams.NULL)) {
621            matrixParams.removeAll(matrix.keys()[i]);
622            matrixParams.put(matrix.keys()[i], null);
623         } else {
624            matrixParams.put(matrix.keys()[i], Strings2.replaceTokens(matrix.values()[i], tokenValues));
625         }
626      }
627   }
628 
629   private void addMapPayload(Map<String, String> postParams, PayloadParams mapDefaults,
630            Collection<Entry<String, String>> tokenValues) {
631      for (int i = 0; i < mapDefaults.keys().length; i++) {
632         if (mapDefaults.values()[i].equals(PayloadParams.NULL)) {
633            postParams.put(mapDefaults.keys()[i], null);
634         } else {
635            postParams.put(mapDefaults.keys()[i], Strings2.replaceTokens(mapDefaults.values()[i], tokenValues));
636         }
637      }
638   }
639 
640   @VisibleForTesting
641   List<HttpRequestFilter> getFiltersIfAnnotated(Method method) {
642      List<HttpRequestFilter> filters = Lists.newArrayList();
643      if (declaring.isAnnotationPresent(RequestFilters.class)) {
644         for (Class<? extends HttpRequestFilter> clazz : declaring.getAnnotation(RequestFilters.class).value()) {
645            HttpRequestFilter instance = injector.getInstance(clazz);
646            filters.add(instance);
647            logger.trace("adding filter %s from annotation on %s", instance, declaring.getName());
648         }
649      }
650      if (method.isAnnotationPresent(RequestFilters.class)) {
651         if (method.isAnnotationPresent(OverrideRequestFilters.class))
652            filters.clear();
653         for (Class<? extends HttpRequestFilter> clazz : method.getAnnotation(RequestFilters.class).value()) {
654            HttpRequestFilter instance = injector.getInstance(clazz);
655            filters.add(instance);
656            logger.trace("adding filter %s from annotation on %s", instance, method.getName());
657         }
658      }
659      return filters;
660   }
661 
662   @VisibleForTesting
663   public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) {
664      Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
665               methodToIndexOfParamToEndpointParamAnnotations);
666      if (map.size() >= 1 && args.length > 0) {
667         EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0);
668         Function<Object, URI> parser = injector.getInstance(firstAnnotation.parser());
669 
670         if (map.size() == 1) {
671            int index = map.keySet().iterator().next();
672            try {
673               URI returnVal = parser.apply(args[index]);
674               checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index],
675                        method));
676               return returnVal;
677            } catch (NullPointerException e) {
678               throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e);
679            }
680         } else {
681            SortedSet<Integer> keys = newTreeSet(map.keySet());
682            Iterable<Object> argsToParse = transform(keys, new Function<Integer, Object>() {
683 
684               @Override
685               public Object apply(Integer from) {
686                  return args[from];
687               }
688 
689            });
690            try {
691               URI returnVal = parser.apply(argsToParse);
692               checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse,
693                        method));
694               return returnVal;
695            } catch (NullPointerException e) {
696               throw new IllegalArgumentException(String.format("illegal argument in [%s] for method %s", argsToParse,
697                        method), e);
698            }
699         }
700      }
701      return null;
702   }
703 
704   public static URI getEndpointFor(Method method, Object[] args, Injector injector) {
705      URI endpoint = getEndpointInParametersOrNull(method, args, injector);
706      if (endpoint == null) {
707         Endpoint annotation;
708         if (method.isAnnotationPresent(Endpoint.class)) {
709            annotation = method.getAnnotation(Endpoint.class);
710         } else if (method.getDeclaringClass().isAnnotationPresent(Endpoint.class)) {
711            annotation = method.getDeclaringClass().getAnnotation(Endpoint.class);
712         } else {
713            throw new IllegalStateException("no annotations on class or method: " + method);
714         }
715         return injector.getInstance(Key.get(URI.class, annotation.value()));
716      }
717      return endpoint;
718   }
719 
720   public static final TypeLiteral<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeLiteral<ListenableFuture<Boolean>>() {
721   };
722   public static final TypeLiteral<ListenableFuture<String>> futureStringLiteral = new TypeLiteral<ListenableFuture<String>>() {
723   };
724   public static final TypeLiteral<ListenableFuture<Void>> futureVoidLiteral = new TypeLiteral<ListenableFuture<Void>>() {
725   };
726   public static final TypeLiteral<ListenableFuture<URI>> futureURILiteral = new TypeLiteral<ListenableFuture<URI>>() {
727   };
728   public static final TypeLiteral<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeLiteral<ListenableFuture<InputStream>>() {
729   };
730   public static final TypeLiteral<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeLiteral<ListenableFuture<HttpResponse>>() {
731   };
732 
733   @SuppressWarnings( { "unchecked", "rawtypes" })
734   public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
735      ResponseParser annotation = method.getAnnotation(ResponseParser.class);
736      if (annotation == null) {
737         if (method.getReturnType().equals(void.class)
738                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
739            return Key.get(ReleasePayloadAndReturn.class);
740         } else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
741                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) {
742            return Key.get(ReturnTrueIf2xx.class);
743         } else if (method.getReturnType().equals(InputStream.class)
744                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) {
745            return Key.get(ReturnInputStream.class);
746         } else if (method.getReturnType().equals(HttpResponse.class)
747                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) {
748            return Key.get((Class) IdentityFunction.class);
749         } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) {
750            return getJsonParserKeyForMethod(method);
751         } else if (method.getReturnType().equals(String.class)
752                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
753            return Key.get(ReturnStringIf2xx.class);
754         } else if (method.getReturnType().equals(URI.class)
755                  || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) {
756            return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
757         } else {
758            throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
759         }
760      }
761      return Key.get(annotation.value());
762   }
763 
764   public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Method method) {
765      Type returnVal = getReturnTypeForMethod(method);
766      return getJsonParserKeyForMethodAnType(method, returnVal);
767   }
768 
769   public static Type getReturnTypeForMethod(Method method) {
770      Type returnVal;
771      if (method.getReturnType().getTypeParameters().length == 0) {
772         returnVal = method.getReturnType();
773      } else if (method.getReturnType().equals(ListenableFuture.class)) {
774         ParameterizedType futureType = ((ParameterizedType) method.getGenericReturnType());
775         returnVal = futureType.getActualTypeArguments()[0];
776         if (returnVal instanceof WildcardType)
777            returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
778      } else {
779         returnVal = method.getGenericReturnType();
780      }
781      return returnVal;
782   }
783 
784   @SuppressWarnings( { "unchecked", "rawtypes" })
785   public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethodAnType(Method method, Type returnVal) {
786      ParameterizedType parserType;
787      if (method.isAnnotationPresent(Unwrap.class)) {
788         int depth = method.getAnnotation(Unwrap.class).depth();
789         Class edgeCollection = method.getAnnotation(Unwrap.class).edgeCollection();
790         if (depth == 1 && edgeCollection == Map.class)
791            parserType = Types.newParameterizedType(UnwrapOnlyJsonValue.class, returnVal);
792         else if (depth == 2 && edgeCollection == Map.class)
793            parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, returnVal);
794         else if (depth == 2 && edgeCollection == Set.class)
795            parserType = Types.newParameterizedType(UnwrapOnlyJsonValueInSet.class, returnVal);
796         else if (depth == 3 && edgeCollection == Set.class)
797            parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal);
798         else
799            throw new IllegalStateException(String.format("depth(%d) edgeCollection(%s) not yet supported for @Unwrap",
800                     depth, edgeCollection));
801      } else {
802         parserType = Types.newParameterizedType(ParseJson.class, returnVal);
803      }
804      return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
805   }
806 
807   public static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Method method) {
808      XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
809      if (annotation != null) {
810         return annotation.value();
811      }
812      return null;
813   }
814 
815   public org.jclouds.rest.MapBinder getMapPayloadBinderOrNull(Method method, Object... args) {
816      if (args != null) {
817         for (Object arg : args) {
818            if (arg instanceof Object[]) {
819               Object[] postBinders = (Object[]) arg;
820               if (postBinders.length == 0) {
821               } else if (postBinders.length == 1) {
822                  if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
823                     org.jclouds.rest.MapBinder binder = (org.jclouds.rest.MapBinder) postBinders[0];
824                     injector.injectMembers(binder);
825                     return binder;
826                  }
827               } else {
828                  if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
829                     throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: "
830                              + method.getName());
831                  }
832               }
833            } else if (arg instanceof org.jclouds.rest.MapBinder) {
834               org.jclouds.rest.MapBinder binder = (org.jclouds.rest.MapBinder) arg;
835               injector.injectMembers(binder);
836               return binder;
837            }
838         }
839      }
840      if (method.isAnnotationPresent(MapBinder.class)) {
841         return injector.getInstance(method.getAnnotation(MapBinder.class).value());
842      } else if (method.isAnnotationPresent(org.jclouds.rest.annotations.Payload.class)) {
843         return injector.getInstance(BindMapToStringPayload.class);
844      }
845      return null;
846   }
847 
848   public static Set<String> getHttpMethods(Method method) {
849      Builder<String> methodsBuilder = ImmutableSet.<String> builder();
850      for (Annotation annotation : method.getAnnotations()) {
851         HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class);
852         if (http != null)
853            methodsBuilder.add(http.value());
854      }
855      Set<String> methods = methodsBuilder.build();
856      return (methods.size() == 0) ? null : methods;
857   }
858 
859   public String getHttpMethodOrConstantOrThrowException(Method method) {
860      Set<String> requests = getHttpMethods(method);
861      if (requests == null || requests.size() != 1) {
862         throw new IllegalStateException(
863                  "You must use at least one, but no more than one http method or pathparam annotation on: "
864                           + method.toString());
865      }
866      return requests.iterator().next();
867   }
868 
869   public boolean shouldAddHostHeader(Method method) {
870      if (declaring.isAnnotationPresent(VirtualHost.class) || method.isAnnotationPresent(VirtualHost.class)) {
871         return true;
872      }
873      return false;
874   }
875 
876   private static final Predicate<Set<?>> notEmpty = new Predicate<Set<?>>() {
877      public boolean apply(Set<?> input) {
878         return input.size() >= 1;
879      }
880   };
881 
882   public GeneratedHttpRequest<T> decorateRequest(GeneratedHttpRequest<T> request) {
883      OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(//
884               filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty)
885                        .entrySet(), //
886               filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) {
887         boolean shouldBreak = false;
888         Annotation annotation = Iterables.get(entry.getValue(), 0);
889         Binder binder;
890         if (annotation instanceof BinderParam)
891            binder = injector.getInstance(BinderParam.class.cast(annotation).value());
892         else
893            binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create(
894                     WrapWith.class.cast(annotation).value());
895         if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) {
896            Object input;
897            Class<?> parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()];
898            Class<? extends Object> argType = request.getArgs().get(entry.getKey()).getClass();
899            if (!argType.isArray() && request.getJavaMethod().isVarArgs() && parameterType.isArray()) {
900               int arrayLength = request.getArgs().size() - request.getJavaMethod().getParameterTypes().length + 1;
901               if (arrayLength == 0)
902                  break OUTER;
903               input = (Object[]) Array.newInstance(request.getArgs().get(entry.getKey()).getClass(), arrayLength);
904               System.arraycopy(request.getArgs().toArray(), entry.getKey(), input, 0, arrayLength);
905               shouldBreak = true;
906            } else if (argType.isArray() && request.getJavaMethod().isVarArgs() && parameterType.isArray()) {
907               input = request.getArgs().get(entry.getKey());
908            } else {
909               input = request.getArgs().get(entry.getKey());
910               if (input.getClass().isArray()) {
911                  Object[] payloadArray = (Object[]) input;
912                  input = payloadArray.length > 0 ? payloadArray[0] : null;
913               }
914            }
915            if (input != null) {
916               request = binder.bindToRequest(request, input);
917            }
918            if (shouldBreak)
919               break OUTER;
920         }
921      }
922 
923      return request;
924   }
925 
926   public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
927            Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
928      Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
929      if (indexToPayloadAnnotation.size() > 1) {
930         throw new IllegalStateException(String.format(
931                  "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(),
932                  indexToPayloadAnnotation));
933      }
934      return indexToPayloadAnnotation;
935   }
936 
937   private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
938            Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
939      Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method),
940               new Predicate<Set<Annotation>>() {
941                  public boolean apply(Set<Annotation> input) {
942                     return input.size() == 1;
943                  }
944               });
945      return indexToPayloadAnnotation;
946   }
947 
948   private HttpRequestOptions findOptionsIn(Method method, Object... args) {
949      for (int index : methodToIndexesOfOptions.get(method)) {
950         if (args.length >= index + 1) {// accomodate varargs
951            if (args[index] instanceof Object[]) {
952               Object[] options = (Object[]) args[index];
953               if (options.length == 0) {
954               } else if (options.length == 1) {
955                  if (options[0] instanceof HttpRequestOptions) {
956                     HttpRequestOptions binder = (HttpRequestOptions) options[0];
957                     injector.injectMembers(binder);
958                     return binder;
959                  }
960               } else {
961                  if (options[0] instanceof HttpRequestOptions) {
962                     throw new IllegalArgumentException("we currently do not support multiple varargs options in: "
963                              + method.getName());
964                  }
965               }
966            } else {
967               return (HttpRequestOptions) args[index];
968            }
969         }
970      }
971      return null;
972   }
973 
974   public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
975            final Object... args) {
976      Multimap<String, String> headers = LinkedHashMultimap.create();
977      addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
978      Map<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method);
979      for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.entrySet()) {
980         for (Annotation key : entry.getValue()) {
981            String value = args[entry.getKey()].toString();
982            value = Strings2.replaceTokens(value, tokenValues);
983            headers.put(((HeaderParam) key).value(), value);
984         }
985      }
986      addProducesIfPresentOnTypeOrMethod(headers, method);
987      addConsumesIfPresentOnTypeOrMethod(headers, method);
988      return headers;
989   }
990 
991   void addConsumesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Method method) {
992      List<String> accept = getAcceptHeadersOrNull(method);
993      if (accept.size() > 0)
994         headers.replaceValues(ACCEPT, accept);
995   }
996 
997   private static List<String> getAcceptHeadersOrNull(Method method) {
998      List<String> accept = Collections.emptyList();
999      if (method.getDeclaringClass().isAnnotationPresent(Consumes.class)) {
1000         Consumes header = method.getDeclaringClass().getAnnotation(Consumes.class);
1001         accept = asList(header.value());
1002      }
1003      if (method.isAnnotationPresent(Consumes.class)) {
1004         Consumes header = method.getAnnotation(Consumes.class);
1005         accept = asList(header.value());
1006      }
1007      return accept;
1008   }
1009 
1010   void addProducesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Method method) {
1011      if (declaring.isAnnotationPresent(Produces.class)) {
1012         Produces header = declaring.getAnnotation(Produces.class);
1013         headers.replaceValues(CONTENT_TYPE, asList(header.value()));
1014      }
1015      if (method.isAnnotationPresent(Produces.class)) {
1016         Produces header = method.getAnnotation(Produces.class);
1017         headers.replaceValues(CONTENT_TYPE, asList(header.value()));
1018      }
1019   }
1020 
1021   public void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Method method,
1022            Collection<Entry<String, String>> tokenValues) {
1023      if (declaring.isAnnotationPresent(Headers.class)) {
1024         Headers header = declaring.getAnnotation(Headers.class);
1025         addHeader(headers, header, tokenValues);
1026      }
1027      if (method.isAnnotationPresent(Headers.class)) {
1028         Headers header = method.getAnnotation(Headers.class);
1029         addHeader(headers, header, tokenValues);
1030      }
1031   }
1032 
1033   private void addHeader(Multimap<String, String> headers, Headers header,
1034            Collection<Entry<String, String>> tokenValues) {
1035      for (int i = 0; i < header.keys().length; i++) {
1036         String value = header.values()[i];
1037         value = Strings2.replaceTokens(value, tokenValues);
1038         headers.put(header.keys()[i], value);
1039      }
1040 
1041   }
1042 
1043   List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) {
1044      List<Part> parts = newLinkedList();
1045      Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method);
1046      for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.entrySet()) {
1047         for (Annotation key : entry.getValue()) {
1048            PartParam param = (PartParam) key;
1049            PartOptions options = new PartOptions();
1050            if (!PartParam.NO_CONTENT_TYPE.equals(param.contentType()))
1051               options.contentType(param.contentType());
1052            if (!PartParam.NO_FILENAME.equals(param.filename()))
1053               options.filename(Strings2.replaceTokens(param.filename(), iterable));
1054            Part part = Part.create(param.name(), newPayload(args[entry.getKey()]), options);
1055            parts.add(part);
1056         }
1057      }
1058      return parts;
1059   }
1060 
1061   public static HttpRequest findHttpRequestInArgs(Object[] args) {
1062      if (args == null)
1063         return null;
1064      for (int i = 0; i < args.length; i++)
1065         if (args[i] instanceof HttpRequest)
1066            return HttpRequest.class.cast(args[i]);
1067      return null;
1068   }
1069 
1070   public static Payload findPayloadInArgs(Object[] args) {
1071      if (args == null)
1072         return null;
1073      for (int i = 0; i < args.length; i++)
1074         if (args[i] instanceof Payload)
1075            return Payload.class.cast(args[i]);
1076         else if (args[i] instanceof PayloadEnclosing)
1077            return PayloadEnclosing.class.cast(args[i]).getPayload();
1078      return null;
1079   }
1080 
1081   private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) {
1082      Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
1083      Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method);
1084 
1085      Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1086      for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
1087         for (Annotation key : entry.getValue()) {
1088            Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1089            String paramKey = ((PathParam) key).value();
1090            String paramValue;
1091            if (extractors != null && extractors.size() > 0) {
1092               ParamParser extractor = (ParamParser) extractors.iterator().next();
1093               paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1094            } else {
1095               paramValue = args[entry.getKey()].toString();
1096            }
1097            pathParamValues.put(paramKey, paramValue);
1098         }
1099      }
1100 
1101      if (method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1102         String paramKey = method.getAnnotation(PathParam.class).value();
1103         String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1104         pathParamValues.put(paramKey, paramValue);
1105 
1106      }
1107      return pathParamValues;
1108   }
1109 
1110   private Multimap<String, String> encodeValues(Multimap<String, String> unencoded, char... skips) {
1111      Multimap<String, String> encoded = LinkedHashMultimap.create();
1112      for (Entry<String, String> entry : unencoded.entries()) {
1113         encoded.put(entry.getKey(), Strings2.urlEncode(entry.getValue(), skips));
1114      }
1115      return encoded;
1116   }
1117 
1118   private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) {
1119      Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
1120      Map<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method);
1121 
1122      Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1123      for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.entrySet()) {
1124         for (Annotation key : entry.getValue()) {
1125            Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1126            String paramKey = ((MatrixParam) key).value();
1127            String paramValue;
1128            if (extractors != null && extractors.size() > 0) {
1129               ParamParser extractor = (ParamParser) extractors.iterator().next();
1130               paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1131            } else {
1132               paramValue = args[entry.getKey()].toString();
1133            }
1134            matrixParamValues.put(paramKey, paramValue);
1135         }
1136      }
1137 
1138      if (method.isAnnotationPresent(MatrixParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1139         String paramKey = method.getAnnotation(MatrixParam.class).value();
1140         String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1141         matrixParamValues.put(paramKey, paramValue);
1142 
1143      }
1144      return matrixParamValues;
1145   }
1146 
1147   private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) {
1148      Multimap<String, String> formParamValues = LinkedHashMultimap.create();
1149      Map<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method);
1150 
1151      Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1152      for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.entrySet()) {
1153         for (Annotation key : entry.getValue()) {
1154            Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1155            String paramKey = ((FormParam) key).value();
1156            String paramValue;
1157            if (extractors != null && extractors.size() > 0) {
1158               ParamParser extractor = (ParamParser) extractors.iterator().next();
1159               paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1160            } else {
1161               paramValue = args[entry.getKey()].toString();
1162            }
1163            formParamValues.put(paramKey, paramValue);
1164         }
1165      }
1166 
1167      if (method.isAnnotationPresent(FormParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1168         String paramKey = method.getAnnotation(FormParam.class).value();
1169         String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1170         formParamValues.put(paramKey, paramValue);
1171 
1172      }
1173      return formParamValues;
1174   }
1175 
1176   private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) {
1177      Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
1178      Map<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method);
1179 
1180      Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1181      for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.entrySet()) {
1182         for (Annotation key : entry.getValue()) {
1183            Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1184            String paramKey = ((QueryParam) key).value();
1185            String paramValue;
1186            if (extractors != null && extractors.size() > 0) {
1187               ParamParser extractor = (ParamParser) extractors.iterator().next();
1188               paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1189            } else {
1190               paramValue = args[entry.getKey()].toString();
1191            }
1192            queryParamValues.put(paramKey, paramValue);
1193         }
1194      }
1195 
1196      if (method.isAnnotationPresent(QueryParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1197         String paramKey = method.getAnnotation(QueryParam.class).value();
1198         String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1199         queryParamValues.put(paramKey, paramValue);
1200 
1201      }
1202      return queryParamValues;
1203   }
1204 
1205   private Map<String, String> buildPostParams(Method method, Object... args) {
1206      Map<String, String> postParams = newHashMap();
1207      Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method);
1208      Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1209      for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
1210         for (Annotation key : entry.getValue()) {
1211            Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1212            String paramKey = ((PayloadParam) key).value();
1213            String paramValue;
1214            if (extractors != null && extractors.size() > 0) {
1215               ParamParser extractor = (ParamParser) extractors.iterator().next();
1216               paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1217            } else {
1218               paramValue = args[entry.getKey()] != null ? args[entry.getKey()].toString() : null;
1219            }
1220            postParams.put(paramKey, paramValue);
1221 
1222         }
1223      }
1224      return postParams;
1225   }
1226 
1227}

[all classes][org.jclouds.rest.internal]
EMMA 2.0.5312 (C) Vladimir Roubtsov