View Javadoc

1   package com.atlassian.plugins.rest.doclet.generators.schema;
2   
3   import com.atlassian.rest.annotation.RestProperty;
4   import com.atlassian.rest.annotation.RestProperty.Scope;
5   import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6   import com.google.common.collect.ImmutableSet;
7   import org.codehaus.jackson.annotate.JsonAutoDetect;
8   import org.codehaus.jackson.annotate.JsonIgnore;
9   import org.codehaus.jackson.annotate.JsonProperty;
10  
11  import javax.xml.bind.annotation.XmlAttribute;
12  import javax.xml.bind.annotation.XmlElement;
13  import javax.xml.bind.annotation.XmlRootElement;
14  import javax.xml.bind.annotation.XmlTransient;
15  import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
16  import java.beans.IntrospectionException;
17  import java.beans.Introspector;
18  import java.beans.PropertyDescriptor;
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.AnnotatedElement;
21  import java.lang.reflect.Field;
22  import java.lang.reflect.Method;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.Optional;
26  import java.util.Set;
27  
28  import static com.atlassian.plugins.rest.doclet.generators.schema.Types.isJDKClass;
29  import static com.google.common.base.Strings.emptyToNull;
30  
31  public final class Annotations {
32      private Annotations() {
33      }
34  
35      private static final Set<Class<? extends Annotation>> propertyAnnotations = ImmutableSet.of(
36              JsonProperty.class,
37              com.fasterxml.jackson.annotation.JsonProperty.class,
38              XmlAttribute.class,
39              XmlElement.class,
40              XmlJavaTypeAdapter.class
41      );
42  
43      private static final Set<Class<? extends Annotation>> autodetectAnnotations = ImmutableSet.of(
44              JsonAutoDetect.class,
45              com.fasterxml.jackson.annotation.JsonAutoDetect.class,
46              XmlRootElement.class
47      );
48  
49      private static final Set<Class<? extends Annotation>> ignoreAnnotations = ImmutableSet.of(
50              JsonIgnore.class,
51              com.fasterxml.jackson.annotation.JsonIgnore.class,
52              XmlTransient.class
53      );
54  
55      public static boolean shouldFieldBeIncludedInSchema(final AnnotatedElement element, String name, final Class<?> modelClass, final Scope scope) {
56          boolean isJsonField = isCustomInterface(modelClass) || isAnyAnnotationPresent(element, propertyAnnotations) ||
57                  element instanceof Field && isAnyAnnotationPresent(modelClass, autodetectAnnotations);
58  
59          Scope fieldScope = element.isAnnotationPresent(RestProperty.class) ? element.getAnnotation(RestProperty.class).scope() : Scope.AUTO;
60  
61          return isJsonField && !isIgnored(name, modelClass) && scope.includes(fieldScope, name);
62      }
63  
64      private static boolean isIgnored(final String name, final Class<?> modelClass) {
65          try {
66              for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors(modelClass)) {
67                  Method getter = propertyDescriptor.getReadMethod();
68                  if (getter != null && propertyDescriptor.getName().equals(name) && isAnyAnnotationPresent(getter, ignoreAnnotations)) {
69                      return true;
70                  }
71              }
72  
73              Field field = modelClass.getDeclaredField(name);
74              return field != null && isAnyAnnotationPresent(field, ignoreAnnotations);
75          } catch (NoSuchFieldException ex) {
76              return false;
77          }
78      }
79  
80      private static boolean isCustomInterface(final Class<?> modelClass) {
81          return !isJDKClass(modelClass) && modelClass.isInterface();
82      }
83  
84      public static String resolveFieldName(final AnnotatedElement element, String defaultName) {
85          if (element.isAnnotationPresent(JsonProperty.class)) {
86              return Optional.ofNullable(emptyToNull(element.getAnnotation(JsonProperty.class).value())).orElse(defaultName);
87          } else if (element.isAnnotationPresent(XmlElement.class)) {
88              String name = element.getAnnotation(XmlElement.class).name();
89              return !name.equals("##default") ? name : defaultName;
90          } else if (element.isAnnotationPresent(XmlAttribute.class)) {
91              String name = element.getAnnotation(XmlAttribute.class).name();
92              return !name.equals("##default") ? name : defaultName;
93          } else {
94              return defaultName;
95          }
96      }
97  
98      public static boolean isRequired(final AnnotatedElement element) {
99          return element.isAnnotationPresent(com.fasterxml.jackson.annotation.JsonProperty.class) &&
100                 element.getAnnotation(com.fasterxml.jackson.annotation.JsonProperty.class).required() ||
101                 element.isAnnotationPresent(RestProperty.class) && element.getAnnotation(RestProperty.class).required();
102     }
103 
104     public static String getDescription(final AnnotatedElement field) {
105         if (field != null) {
106             if (field.isAnnotationPresent(JsonPropertyDescription.class)) {
107                 return emptyToNull(field.getAnnotation(JsonPropertyDescription.class).value());
108             } else if (field.isAnnotationPresent(RestProperty.class)) {
109                 return emptyToNull(field.getAnnotation(RestProperty.class).description());
110             }
111         }
112         return null;
113     }
114 
115     private static boolean isAnyAnnotationPresent(AnnotatedElement element, Iterable<Class<? extends Annotation>> annotations) {
116         for (Class<? extends Annotation> annotation : annotations) {
117             if (element.isAnnotationPresent(annotation)) {
118                 return true;
119             }
120         }
121         return false;
122     }
123 
124     private static Iterable<PropertyDescriptor> getPropertyDescriptors(Class<?> type) {
125         try {
126             return Arrays.asList(Introspector.getBeanInfo(type).getPropertyDescriptors());
127         } catch (IntrospectionException e) {
128             return Collections.emptyList();
129         }
130     }
131 }