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.google.common.base.Strings.emptyToNull;
29  
30  public final class Annotations {
31      private Annotations() {
32      }
33  
34      private static final Set<Class<? extends Annotation>> propertyAnnotations = ImmutableSet.of(
35              JsonProperty.class,
36              com.fasterxml.jackson.annotation.JsonProperty.class,
37              XmlAttribute.class,
38              XmlElement.class,
39              XmlJavaTypeAdapter.class
40      );
41  
42      private static final Set<Class<? extends Annotation>> autodetectAnnotations = ImmutableSet.of(
43              JsonAutoDetect.class,
44              com.fasterxml.jackson.annotation.JsonAutoDetect.class,
45              XmlRootElement.class
46      );
47  
48      private static final Set<Class<? extends Annotation>> ignoreAnnotations = ImmutableSet.of(
49              JsonIgnore.class,
50              com.fasterxml.jackson.annotation.JsonIgnore.class,
51              XmlTransient.class
52      );
53  
54      public static boolean shouldFieldBeIncludedInSchema(final AnnotatedElement element, String name, final Class<?> modelClass, final Scope scope) {
55          boolean isJsonField = isCustomInterface(modelClass) || isAnyAnnotationPresent(element, propertyAnnotations) ||
56                  element instanceof Field && isAnyAnnotationPresent(modelClass, autodetectAnnotations);
57  
58          Scope fieldScope = element.isAnnotationPresent(RestProperty.class) ? element.getAnnotation(RestProperty.class).scope() : Scope.AUTO;
59  
60          return isJsonField && !isIgnored(name, modelClass) && scope.includes(fieldScope, name);
61      }
62  
63      private static boolean isIgnored(final String name, final Class<?> modelClass) {
64          try {
65              for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors(modelClass)) {
66                  Method getter = propertyDescriptor.getReadMethod();
67                  if (getter != null && propertyDescriptor.getName().equals(name) && isAnyAnnotationPresent(getter, ignoreAnnotations)) {
68                      return true;
69                  }
70              }
71  
72              Field field = modelClass.getDeclaredField(name);
73              return field != null && isAnyAnnotationPresent(field, ignoreAnnotations);
74          } catch (NoSuchFieldException ex) {
75              return false;
76          }
77      }
78  
79      private static boolean isCustomInterface(final Class<?> modelClass) {
80          return !modelClass.getCanonicalName().startsWith("java") && modelClass.isInterface();
81      }
82  
83      public static String resolveFieldName(final AnnotatedElement element, String defaultName) {
84          if (element.isAnnotationPresent(JsonProperty.class)) {
85              return Optional.ofNullable(emptyToNull(element.getAnnotation(JsonProperty.class).value())).orElse(defaultName);
86          } else if (element.isAnnotationPresent(XmlElement.class)) {
87              String name = element.getAnnotation(XmlElement.class).name();
88              return !name.equals("##default") ? name : defaultName;
89          } else if (element.isAnnotationPresent(XmlAttribute.class)) {
90              String name = element.getAnnotation(XmlAttribute.class).name();
91              return !name.equals("##default") ? name : defaultName;
92          } else {
93              return defaultName;
94          }
95      }
96  
97      public static boolean isRequired(final AnnotatedElement element) {
98  
99          return element.isAnnotationPresent(com.fasterxml.jackson.annotation.JsonProperty.class) &&
100                 element.getAnnotation(com.fasterxml.jackson.annotation.JsonProperty.class).required();
101     }
102 
103     public static String getDescription(final AnnotatedElement field) {
104         return field != null && field.isAnnotationPresent(JsonPropertyDescription.class) ?
105                 emptyToNull(field.getAnnotation(JsonPropertyDescription.class).value()) : null;
106     }
107 
108     private static boolean isAnyAnnotationPresent(AnnotatedElement element, Iterable<Class<? extends Annotation>> annotations) {
109         for (Class<? extends Annotation> annotation : annotations) {
110             if (element.isAnnotationPresent(annotation)) {
111                 return true;
112             }
113         }
114         return false;
115     }
116 
117     private static Iterable<PropertyDescriptor> getPropertyDescriptors(Class<?> type) {
118         try {
119             return Arrays.asList(Introspector.getBeanInfo(type).getPropertyDescriptors());
120         } catch (IntrospectionException e) {
121             return Collections.emptyList();
122         }
123     }
124 }