1 package com.atlassian.plugins.rest.common.expand;
2
3 import com.atlassian.plugins.rest.common.expand.parameter.ExpandParameter;
4 import com.atlassian.plugins.rest.common.expand.resolver.EntityExpanderResolver;
5 import com.atlassian.plugins.rest.common.util.ReflectionUtils;
6 import static com.atlassian.plugins.rest.common.util.ReflectionUtils.*;
7 import com.google.common.base.Predicate;
8 import com.google.common.collect.Collections2;
9 import org.apache.commons.lang.StringUtils;
10
11 import javax.xml.bind.annotation.XmlAttribute;
12 import javax.xml.bind.annotation.XmlElement;
13 import java.lang.annotation.Annotation;
14 import java.lang.reflect.Field;
15 import java.util.Collection;
16
17
18
19
20 public class EntityCrawler
21 {
22
23
24
25
26
27
28 public void crawl(Object entity, ExpandParameter expandParameter, EntityExpanderResolver expanderResolver)
29 {
30 if (entity == null)
31 {
32 return;
33 }
34
35 final Collection<Field> expandableFields = getExpandableFields(entity);
36 setExpandParameter(expandableFields, entity);
37 expandFields(expandableFields, entity, expandParameter, expanderResolver);
38 }
39
40 private void setExpandParameter(Collection<Field> expandableFields, Object entity)
41 {
42 final Field expand = getExpandField(entity);
43 if (expand != null && !expandableFields.isEmpty())
44 {
45 final StringBuilder expandValue = new StringBuilder();
46 for (Field field : expandableFields)
47 {
48 expandValue.append(getExpandable(field).value()).append(",");
49 }
50 expandValue.deleteCharAt(expandValue.length() - 1);
51
52 setFieldValue(expand, entity, expandValue.toString());
53 }
54 }
55
56 private Field getExpandField(Object entity)
57 {
58 for (Field field : getDeclaredFields(entity.getClass()))
59 {
60 if (field.getType().equals(String.class))
61 {
62 final XmlAttribute annotation = field.getAnnotation(XmlAttribute.class);
63 if (annotation != null && (field.getName().equals("expand") || "expand".equals(annotation.name())))
64 {
65 return field;
66 }
67 }
68 }
69 return null;
70 }
71
72 private Collection<Field> getExpandableFields(final Object entity)
73 {
74 return Collections2.filter(getDeclaredFields(entity.getClass()), new Predicate<Field>()
75 {
76 public boolean apply(Field field)
77 {
78 return getExpandable(field) != null && ReflectionUtils.getFieldValue(field, entity) != null;
79 }
80 });
81 }
82
83 private void expandFields(Collection<Field> expandableFields, Object entity, ExpandParameter expandParameter, EntityExpanderResolver expanderResolver)
84 {
85 for (Field field : expandableFields)
86 {
87 final Expandable expandable = getExpandable(field);
88 if (expandParameter.shouldExpand(expandable) && expanderResolver.hasExpander(field.getType()))
89 {
90
91 final EntityExpander<Object> entityExpander = expanderResolver.getExpander(field.getType());
92
93 final ExpandContext<Object> context = new DefaultExpandContext<Object>(getFieldValue(field, entity), expandable, expandParameter);
94 setFieldValue(field, entity, entityExpander.expand(context, expanderResolver, this));
95 }
96 }
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110 Expandable getExpandable(final Field field)
111 {
112 if (field == null)
113 {
114 return null;
115 }
116
117 final Expandable expandable = field.getAnnotation(Expandable.class);
118 if (expandable == null)
119 {
120 return null;
121 }
122
123 if (StringUtils.isNotEmpty(expandable.value()))
124 {
125 return expandable;
126 }
127
128 final XmlElement xmlElement = field.getAnnotation(XmlElement.class);
129 if (xmlElement != null && StringUtils.isNotEmpty(xmlElement.name()) && !StringUtils.equals("##default", xmlElement.name()))
130 {
131 return new ExpandableWithValue(xmlElement.name());
132 }
133
134 return new ExpandableWithValue(field.getName());
135 }
136
137 private static class ExpandableWithValue implements Expandable
138 {
139 private final String value;
140
141 public ExpandableWithValue(String value)
142 {
143 this.value = value;
144 }
145
146 public String value()
147 {
148 return value;
149 }
150
151 public Class<? extends Annotation> annotationType()
152 {
153 return Expandable.class;
154 }
155 }
156 }