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 }