1 package com.atlassian.plugins.rest.doclet.generators.resourcedoc;
2
3 import java.lang.reflect.AnnotatedElement;
4 import java.lang.reflect.Method;
5 import java.util.Arrays;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.List;
9 import java.util.Optional;
10 import javax.ws.rs.core.Response;
11
12 import com.atlassian.annotations.ExperimentalApi;
13 import com.atlassian.plugins.rest.doclet.generators.schema.RichClass;
14 import com.atlassian.rest.annotation.RequestType;
15 import com.atlassian.rest.annotation.ResponseType;
16 import com.atlassian.rest.annotation.ResponseTypes;
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.Lists;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import static com.atlassian.rest.annotation.ResponseType.StatusType.SUCCESS;
23 import static java.util.Optional.empty;
24
25 public class RestMethod
26 {
27 private static final Logger log = LoggerFactory.getLogger(RestMethod.class);
28
29 private final Class<?> resourceClass;
30 private final Method method;
31
32 private RestMethod(final Class<?> resourceClass, final Method method)
33 {
34 this.resourceClass = resourceClass;
35 this.method = method;
36 }
37
38 public boolean isExperimental()
39 {
40 return method.getAnnotation(ExperimentalApi.class) != null;
41 }
42
43 public static RestMethod restMethod(Class<?> resourceClass, final Method method)
44 {
45 return new RestMethod(resourceClass, method);
46 }
47
48 public Optional<RichClass> getRequestType()
49 {
50 Optional<RichClass> typeFromAnnotation = empty();
51 Optional<RichClass> typeFromParameter = empty();
52
53 if (method.isAnnotationPresent(RequestType.class))
54 {
55 RequestType requestType = method.getAnnotation(RequestType.class);
56 typeFromAnnotation = Optional.of(RichClass.of(requestType.value(), requestType.genericTypes()));
57 }
58
59 for (int i = 0; i < method.getParameterTypes().length; i++)
60 {
61 if (method.getParameterAnnotations()[i].length == 0)
62 {
63 typeFromParameter = Optional.of(RichClass.of(method.getGenericParameterTypes()[i]));
64 break;
65 }
66 }
67
68 if (typeFromAnnotation.isPresent() && !typeFromAnnotation.equals(typeFromParameter))
69 {
70 log.warn(String.format("Method %s.%s declares request type that is different than the actual request "
71 + "parameter of this method. This may result in inaccurate documentation.", resourceClass.getSimpleName(), method.getName()));
72 }
73
74 return typeFromAnnotation.isPresent() ? typeFromAnnotation : typeFromParameter;
75 }
76
77 public List<RichClass> responseTypesFor(int status)
78 {
79 List<RichClass> types = Lists.newArrayList();
80
81 for (ResponseType responseType : declaredResponseTypes())
82 {
83 if (status == responseType.status() || responseType.statusType().matches(status))
84 {
85 if (!Void.class.equals(responseType.value()))
86 {
87 types.add(RichClass.of(responseType.value(), responseType.genericTypes()));
88 }
89 }
90 }
91
92 if (SUCCESS.matches(status) &&
93 !method.getReturnType().equals(Response.class) &&
94 !"void".equals(method.getGenericReturnType().getTypeName()))
95 {
96 RichClass actualReturnType = RichClass.of(method.getGenericReturnType());
97
98 if (!types.isEmpty() && !Collections.singletonList(actualReturnType).equals(types))
99 {
100 log.warn(String.format("Method %s.%s declares response type for success response that is different than the actual return "
101 + "type of this method. This may result in inaccurate documentation.", resourceClass.getSimpleName(), method.getName()));
102 }
103 else
104 {
105 return Collections.singletonList(actualReturnType);
106 }
107 }
108
109 return ImmutableList.copyOf(types);
110 }
111
112 private Iterable<ResponseType> declaredResponseTypes()
113 {
114 List<ResponseType> responseTypes = Lists.newArrayList();
115
116 responseTypes.addAll(responseTypes(method));
117 responseTypes.addAll(responseTypes(resourceClass));
118
119 return responseTypes;
120 }
121
122 private Collection<ResponseType> responseTypes(final AnnotatedElement element)
123 {
124 if (element.isAnnotationPresent(ResponseType.class))
125 {
126 return Collections.singleton(element.getAnnotation(ResponseType.class));
127 }
128
129 if (element.isAnnotationPresent(ResponseTypes.class))
130 {
131 return Arrays.asList(element.getAnnotation(ResponseTypes.class).value());
132 }
133
134 return Collections.emptyList();
135 }
136 }