View Javadoc

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