1 package com.atlassian.marketplace.client.impl;
2
3 import java.lang.reflect.Field;
4 import java.net.URI;
5 import java.util.HashMap;
6 import java.util.Map;
7 import java.util.concurrent.ConcurrentHashMap;
8
9 import com.atlassian.fugue.Option;
10 import com.atlassian.marketplace.client.encoding.MissingRequiredField;
11 import com.atlassian.marketplace.client.encoding.SchemaViolation;
12 import com.atlassian.marketplace.client.model.Links;
13 import com.atlassian.marketplace.client.model.RequiredLink;
14
15 import com.google.common.collect.ImmutableList;
16 import com.google.gson.JsonParseException;
17
18 import static com.atlassian.fugue.Option.none;
19 import static com.google.common.collect.Iterables.concat;
20 import static com.google.common.collect.Iterables.isEmpty;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public abstract class EntityValidator
39 {
40 private static ConcurrentHashMap<Class<?>, Map<String, Field>> classFields =
41 new ConcurrentHashMap<Class<?>, Map<String,Field>>();
42
43 public static <T> T validateInstance(T instance) throws SchemaViolationException
44 {
45 Iterable<SchemaViolation> violations = ImmutableList.of();
46 for (Field f: getClassFields(instance.getClass()).values())
47 {
48 violations = concat(violations, postProcessField(f, instance));
49 }
50 if (!isEmpty(violations))
51 {
52 throw new SchemaViolationException(violations);
53 }
54 return instance;
55 }
56
57 private static Iterable<SchemaViolation> postProcessField(Field f, Object o)
58 {
59 f.setAccessible(true);
60 try
61 {
62 if (f.get(o) == null)
63 {
64
65 if (Option.class.isAssignableFrom(f.getType()))
66 {
67
68 f.set(o, none());
69 }
70 else
71 {
72 RequiredLink reqLinkAnno = f.getAnnotation(RequiredLink.class);
73 if (reqLinkAnno != null)
74 {
75
76 return setRequiredLinkField(reqLinkAnno, f, o);
77 }
78 else
79 {
80
81 return Option.<SchemaViolation>some(new MissingRequiredField(o.getClass(), f.getName()));
82 }
83 }
84 }
85
86 return none();
87 }
88 catch (IllegalAccessException e)
89 {
90
91 throw new JsonParseException(e);
92 }
93 }
94
95 private static Iterable<SchemaViolation> setRequiredLinkField(RequiredLink anno, Field f, Object o) throws IllegalAccessException
96 {
97 Field linksField = getClassFields(o.getClass()).get("_links");
98 if (linksField == null || linksField.getType() != Links.class)
99 {
100 throw new IllegalStateException("@RequiredLink annotation was found in a class without a 'Links _links' field");
101 }
102 Links links = (Links) linksField.get(o);
103 for (URI u: links.getUri(anno.rel()))
104 {
105 f.set(o, u);
106 return none();
107 }
108 return Option.<SchemaViolation>some(new MissingRequiredField(o.getClass(), "_links." + anno.rel()));
109 }
110
111 public static Map<String, Field> getClassFields(Class<?> c)
112 {
113
114
115 Map<String, Field> m = classFields.get(c);
116 if (m == null)
117 {
118 m = new HashMap<String, Field>();
119 Class<?> c1 = c;
120 while (c1 != null)
121 {
122 for (Field f: c1.getDeclaredFields())
123 {
124 f.setAccessible(true);
125 m.put(f.getName(), f);
126 }
127 c1 = c1.getSuperclass();
128 }
129 classFields.put(c, m);
130 }
131 return m;
132 }
133 }