View Javadoc

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 }