comparison src/main/java/fj/test/reflect/Check.java @ 0:fe80c1edf1be

add getLoop
author tatsuki
date Fri, 20 Mar 2015 21:04:03 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:fe80c1edf1be
1 package fj.test.reflect;
2
3 import static fj.Bottom.error;
4 import fj.Class;
5 import static fj.Class.clas;
6 import fj.F;
7 import fj.Function;
8 import fj.P;
9 import static fj.P.p;
10 import fj.P2;
11 import fj.P3;
12 import fj.data.Array;
13 import static fj.data.Array.array;
14 import fj.data.List;
15 import static fj.data.List.join;
16 import static fj.data.List.list;
17 import fj.data.Option;
18 import static fj.data.Option.fromNull;
19 import static fj.data.Option.none;
20 import static fj.data.Option.some;
21 import static fj.data.Option.somes;
22 import fj.test.CheckResult;
23 import fj.test.Property;
24 import fj.test.Rand;
25
26 import java.lang.reflect.AnnotatedElement;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Field;
29 import java.lang.reflect.Method;
30 import static java.lang.reflect.Modifier.isStatic;
31
32 /**
33 * Functions for checking properties in a class that are found reflectively and according to various
34 * annotations.
35 *
36 * @version %build.number%
37 */
38 public final class Check {
39 private Check() {
40 throw new UnsupportedOperationException();
41 }
42
43 /**
44 * Returns the results and names of checking the properties on the given classes using a
45 * {@link Rand#standard standard random generator}.
46 *
47 * @param c The classes to check the properties of.
48 * @param categories The categories of properties to return. If no categories are specified, all
49 * candidate properties are returned, otherwise, only those properties in the given categories are
50 * returned (properties in no category are omitted in this latter case).
51 * @return The results and names of checking the properties on the given classes using a
52 * {@link Rand#standard standard random generator}.
53 */
54 public static <T> List<P2<String, CheckResult>> check(final List<java.lang.Class<T>> c, final String... categories) {
55 return check(c, Rand.standard, categories);
56 }
57
58 /**
59 * Returns the results and names of checking the properties on the given classes using a
60 * {@link Rand#standard standard random generator}.
61 *
62 * @param c The classes to check the properties of.
63 * @param categories The categories of properties to return. If no categories are specified, all
64 * candidate properties are returned, otherwise, only those properties in the given categories are
65 * returned (properties in no category are omitted in this latter case).
66 * @return The results and names of checking the properties on the given classes using a
67 * {@link Rand#standard standard random generator}.
68 */
69 public static <T> List<P2<String, CheckResult>> check(final List<java.lang.Class<T>> c, final List<String> categories) {
70 return check(c, Rand.standard, categories.toArray().array(String[].class));
71 }
72
73 /**
74 * Returns the results and names of checking the properties on the given classes.
75 *
76 * @param c The classes to check the properties of.
77 * @param r The random generator to use to check the properties on the given classes.
78 * @param categories The categories of properties to return. If no categories are specified, all
79 * candidate properties are returned, otherwise, only those properties in the given categories are
80 * returned (properties in no category are omitted in this latter case).
81 * @return The results and names of checking the properties on the given classes.
82 */
83 public static <T> List<P2<String, CheckResult>> check(final List<java.lang.Class<T>> c, final Rand r, final String... categories) {
84 return join(c.map(new F<java.lang.Class<T>, List<P2<String, CheckResult>>>() {
85 public List<P2<String, CheckResult>> f(final java.lang.Class<T> c) {
86 return check(c, r, categories);
87 }
88 }));
89 }
90
91 /**
92 * Returns the results and names of checking the properties on the given classes.
93 *
94 * @param c The classes to check the properties of.
95 * @param r The random generator to use to check the properties on the given classes.
96 * @param categories The categories of properties to return. If no categories are specified, all
97 * candidate properties are returned, otherwise, only those properties in the given categories are
98 * returned (properties in no category are omitted in this latter case).
99 * @return The results and names of checking the properties on the given classes.
100 */
101 public static <T> List<P2<String, CheckResult>> check(final List<java.lang.Class<T>> c, final Rand r, final List<String> categories) {
102 return check(c, r, categories.toArray().array(String[].class));
103 }
104
105 /**
106 * Returns the results and names of checking the properties on the given class using a
107 * {@link Rand#standard standard random generator}.
108 *
109 * @param c The class to check the properties of.
110 * @param categories The categories of properties to return. If no categories are specified, all
111 * candidate properties are returned, otherwise, only those properties in the given categories are
112 * returned (properties in no category are omitted in this latter case).
113 * @return The results and names of checking the properties on the given class using a
114 * {@link Rand#standard standard random generator}.
115 */
116 public static <T> List<P2<String, CheckResult>> check(final java.lang.Class<T> c, final String... categories) {
117 return check(c, Rand.standard, categories);
118 }
119
120 /**
121 * Returns the results and names of checking the properties on the given class using a
122 * {@link Rand#standard standard random generator}.
123 *
124 * @param c The class to check the properties of.
125 * @param categories The categories of properties to return. If no categories are specified, all
126 * candidate properties are returned, otherwise, only those properties in the given categories are
127 * returned (properties in no category are omitted in this latter case).
128 * @return The results and names of checking the properties on the given class using a
129 * {@link Rand#standard standard random generator}.
130 */
131 public static <T> List<P2<String, CheckResult>> check(final java.lang.Class<T> c, final List<String> categories) {
132 return check(c, Rand.standard, categories.toArray().array(String[].class));
133 }
134
135 /**
136 * Returns the results and names of checking the properties on the given class.
137 *
138 * @param c The class to check the properties of.
139 * @param r The random generator to use to check the properties on the given class.
140 * @param categories The categories of properties to return. If no categories are specified, all
141 * candidate properties are returned, otherwise, only those properties in the given categories are
142 * returned (properties in no category are omitted in this latter case).
143 * @return The results of checking the properties on the given class.
144 */
145 public static <T> List<P2<String, CheckResult>> check(final java.lang.Class<T> c, final Rand r, final String... categories) {
146 return join(clas(c).inheritance().map(new F<Class<? super T>, List<P3<Property, String, Option<CheckParams>>>>() {
147 public List<P3<Property, String, Option<CheckParams>>> f(final Class<? super T> c) {
148 return properties(c.clas(), categories);
149 }
150 })).map(new F<P3<Property, String, Option<CheckParams>>, P2<String, CheckResult>>() {
151 public P2<String, CheckResult> f(final P3<Property, String, Option<CheckParams>> p) {
152 if(p._3().isSome()) {
153 final CheckParams ps = p._3().some();
154 return p(p._2(), p._1().check(r, ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize()));
155 } else
156 return p(p._2(), p._1().check(r));
157 }
158 });
159 }
160
161 /**
162 * Returns the results and names of checking the properties on the given class.
163 *
164 * @param c The class to check the properties of.
165 * @param r The random generator to use to check the properties on the given class.
166 * @param categories The categories of properties to return. If no categories are specified, all
167 * candidate properties are returned, otherwise, only those properties in the given categories are
168 * returned (properties in no category are omitted in this latter case).
169 * @return The results of checking the properties on the given class.
170 */
171 public static <T> List<P2<String, CheckResult>> check(final java.lang.Class<T> c, final Rand r, final List<String> categories) {
172 return check(c, r, categories.toArray().array(String[].class));
173 }
174
175 /**
176 * Returns all properties, their name and possible check parameters in a given class that are
177 * found reflectively and according to various annotations. For example, properties or their
178 * enclosing class that are annotated with {@link NoCheck} are not considered. The name of a
179 * property is specified by the {@link Name annotation} or if this annotation is not present, the
180 * name of the method or field that represents the property.
181 *
182 * @param c The class to look for properties on.
183 * @param categories The categories of properties to return. If no categories are specified, all
184 * candidate properties are returned, otherwise, only those properties in the given categories are
185 * returned (properties in no category are omitted in this latter case).
186 * @return All properties, their name and possible check parameters in a given class that are
187 * found reflectively and according to various annotations.
188 */
189 public static <U, T extends U> List<P3<Property, String, Option<CheckParams>>> properties(final java.lang.Class<T> c, final String... categories) {
190 //noinspection ClassEscapesDefinedScope
191 final Array<P3<Property, String, Option<CheckParams>>> propFields = properties(array(c.getDeclaredFields()).map(new F<Field, PropertyMember>() {
192 public PropertyMember f(final Field f) {
193 return new PropertyMember() {
194 public java.lang.Class<?> type() {
195 return f.getType();
196 }
197
198 public AnnotatedElement element() {
199 return f;
200 }
201
202 public String name() {
203 return f.getName();
204 }
205
206 public int modifiers() {
207 return f.getModifiers();
208 }
209
210 public <X> Property invoke(final X x) throws IllegalAccessException {
211 f.setAccessible(true);
212 return (Property)f.get(x);
213 }
214
215 public boolean isProperty() {
216 return true;
217 }
218 };
219 }
220 }), c, categories);
221
222 //noinspection ClassEscapesDefinedScope
223 final Array<P3<Property, String, Option<CheckParams>>> propMethods = properties(array(c.getDeclaredMethods()).map(new F<Method, PropertyMember>() {
224 public PropertyMember f(final Method m) {
225 //noinspection ProhibitedExceptionDeclared
226 return new PropertyMember() {
227 public java.lang.Class<?> type() {
228 return m.getReturnType();
229 }
230
231 public AnnotatedElement element() {
232 return m;
233 }
234
235 public String name() {
236 return m.getName();
237 }
238
239 public int modifiers() {
240 return m.getModifiers();
241 }
242
243 public <X> Property invoke(final X x) throws Exception {
244 m.setAccessible(true);
245 return (Property)m.invoke(x);
246 }
247
248 public boolean isProperty() {
249 return m.getParameterTypes().length == 0;
250 }
251 };
252 }
253 }), c, categories);
254
255 return propFields.append(propMethods).toList();
256 }
257
258 private interface PropertyMember {
259 java.lang.Class<?> type();
260 AnnotatedElement element();
261 String name();
262 int modifiers();
263 @SuppressWarnings({"ProhibitedExceptionDeclared"})
264 <X> Property invoke(X x) throws Exception;
265 boolean isProperty();
266 }
267
268 private static <T> Array<P3<Property, String, Option<CheckParams>>> properties(final Array<PropertyMember> ms, final java.lang.Class<T> declaringClass, final String... categories) {
269 final Option<T> t = emptyCtor(declaringClass).map(new F<Constructor<T>, T>() {
270 @SuppressWarnings({"OverlyBroadCatchBlock"})
271 public T f(final Constructor<T> ctor) {
272 try {
273 ctor.setAccessible(true);
274 return ctor.newInstance();
275 } catch(Exception e) {
276 throw error(e.toString());
277 }
278 }
279 });
280
281 final F<AnnotatedElement, F<String, Boolean>> p = new F<AnnotatedElement, F<String, Boolean>>() {
282 public F<String, Boolean> f(final AnnotatedElement e) {
283 return new F<String, Boolean>() {
284 public Boolean f(final String s) {
285 final F<Category, Boolean> p = new F<Category, Boolean>() {
286 public Boolean f(final Category c) {
287 return array(c.value()).exists(new F<String, Boolean>() {
288 public Boolean f(final String cs) {
289 return cs.equals(s);
290 }
291 });
292 }
293 };
294
295 @SuppressWarnings("unchecked")
296 final List<Boolean> bss = somes(list(fromNull(e.getAnnotation(Category.class)).map(p),
297 fromNull(declaringClass.getAnnotation(Category.class)).map(p)));
298 return bss.exists(Function.<Boolean>identity());
299 }
300 };
301 }
302 };
303
304 final F<Name, String> nameS = new F<Name, String>() {
305 public String f(final Name name) {
306 return name.value();
307 }
308 };
309
310 return ms.filter(new F<PropertyMember, Boolean>() {
311 public Boolean f(final PropertyMember m) {
312 //noinspection ObjectEquality
313 return m.isProperty() &&
314 m.type() == Property.class &&
315 !m.element().isAnnotationPresent(NoCheck.class) &&
316 !declaringClass.isAnnotationPresent(NoCheck.class) &&
317 (categories.length == 0 || array(categories).exists(p.f(m.element()))) &&
318 (t.isSome() || isStatic(m.modifiers()));
319 }
320 }).map(new F<PropertyMember, P3<Property, String, Option<CheckParams>>>() {
321 public P3<Property, String, Option<CheckParams>> f(final PropertyMember m) {
322 try {
323 final Option<CheckParams> params = fromNull(m.element().getAnnotation(CheckParams.class)).orElse(fromNull(declaringClass.getAnnotation(CheckParams.class)));
324 final String name = fromNull(m.element().getAnnotation(Name.class)).map(nameS).orSome(m.name());
325 return p(m.invoke(t.orSome(P.<T>p(null))), name, params);
326 } catch(Exception e) {
327 throw error(e.toString());
328 }
329 }
330 });
331 }
332
333 private static <T> Option<Constructor<T>> emptyCtor(final java.lang.Class<T> c) {
334 Option<Constructor<T>> ctor;
335
336 //noinspection UnusedCatchParameter
337 try {
338 ctor = some(c.getDeclaredConstructor());
339 } catch(NoSuchMethodException e) {
340 ctor = none();
341 }
342 return ctor;
343 }
344 }