1 /*
2 * Grapht, an open source dependency injector.
3 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4 * Copyright 2010-2014 Regents of the University of Minnesota
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 51
18 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 package org.grouplens.grapht.annotation;
21
22 import javax.inject.Named;
23 import java.lang.annotation.Annotation;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Proxy;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 /**
30 * <p>
31 * AnnotationBuilder is a "builder" for creating proxy Annotation instances.
32 * This is useful when configuring dependency injection to match a qualifier
33 * annotation that defines attributes, such as the {@link Named} qualifier. As
34 * an example, AnnotationBuilder can be used to construct a Named instance that
35 * matches particular applications of that annotation:
36 *
37 * <pre>
38 * Named proxy = new AnnotationBuilder<Named>(Named.class)
39 * .set("value", "name")
40 * .build();
41 * </pre>
42 * <p>
43 * The above snippet creates an instance of Named that returns the String,
44 * "name", from its value() method. All other methods declared by
45 * {@link Annotation} are implemented correctly given the values configured by
46 * the builder, or the defaults declared in the annotation definition.
47 * <p>
48 * This lets developers define attribute-based qualifiers easily without being
49 * forced to provide an actual annotation implementation that can be used to
50 * create instances.
51 * <p>The proxies returned by this builder are immutable and serializable, like
52 * those returned by {@link java.lang.reflect.AnnotatedElement}.
53 *
54 * @author <a href="http://grouplens.org">GroupLens Research</a>
55 * @param <T> The annotation type created
56 */
57 public final class AnnotationBuilder<T extends Annotation> {
58 private final Map<String, Object> attributes;
59 private final Class<T> type;
60
61 /**
62 * Create a new AnnotationBuilder without any assigned values, that will
63 * create annotations of the given class type.
64 *
65 * @param annotType The annotation class type
66 * @throws NullPointerException if annotType is null
67 * @throws IllegalArgumentException if annotType is not an Annotation class
68 */
69 public AnnotationBuilder(Class<T> annotType) {
70 if (annotType == null) {
71 throw new NullPointerException("Annotation type cannot be null");
72 }
73 if (!annotType.isAnnotation()) {
74 throw new IllegalArgumentException("Class type is not an Annotation: " + annotType);
75 }
76
77 type = annotType;
78 attributes = new HashMap<String, Object>();
79 }
80
81 /**
82 * Constructor method to allow the builder type to be inferred.
83 * @param annotType The annotation type to build.
84 * @param <T> The type of annotation to build (type parameter).
85 * @return An annotation builder to create an implementation of the annotation.
86 */
87 public static <T extends Annotation> AnnotationBuilder<T> of(Class<T> annotType) {
88 return new AnnotationBuilder<T>(annotType);
89 }
90
91 /**
92 * Set the annotation defined member given by <tt>name</tt> to the boolean
93 * <tt>value</tt>.
94 *
95 * @param name The name of the annotation attribute or member to assign
96 * @param value The value to assign to the specified member
97 * @return This builder
98 * @throws NullPointerException if name is null
99 * @throws IllegalArgumentException if name is not a defined member in the
100 * annotation type for this builder, or if the type is not
101 * boolean
102 */
103 public AnnotationBuilder<T> set(String name, boolean value) {
104 return set(name, Boolean.valueOf(value), boolean.class);
105 }
106
107 /**
108 * As {@link #set(String, boolean)} but assigns a byte value.
109 *
110 * @param name
111 * @param value
112 * @return This builder
113 */
114 public AnnotationBuilder<T> set(String name, byte value) {
115 return set(name, Byte.valueOf(value), byte.class);
116 }
117
118 /**
119 * As {@link #set(String, boolean)} but assigns a short value.
120 *
121 * @param name
122 * @param value
123 * @return This builder
124 */
125 public AnnotationBuilder<T> set(String name, short value) {
126 return set(name, Short.valueOf(value), short.class);
127 }
128
129 /**
130 * As {@link #set(String, boolean)} but assigns an int value.
131 *
132 * @param name
133 * @param value
134 * @return This builder
135 */
136 public AnnotationBuilder<T> set(String name, int value) {
137 return set(name, Integer.valueOf(value), int.class);
138 }
139
140 /**
141 * As {@link #set(String, boolean)} but assigns a long value.
142 *
143 * @param name
144 * @param value
145 * @return This builder
146 */
147 public AnnotationBuilder<T> set(String name, long value) {
148 return set(name, Long.valueOf(value), long.class);
149 }
150
151 /**
152 * As {@link #set(String, boolean)} but assigns a char value.
153 *
154 * @param name
155 * @param value
156 * @return This builder
157 */
158 public AnnotationBuilder<T> set(String name, char value) {
159 return set(name, Character.valueOf(value), char.class);
160 }
161
162 /**
163 * As {@link #set(String, boolean)} but assigns a float value.
164 *
165 * @param name
166 * @param value
167 * @return This builder
168 */
169 public AnnotationBuilder<T> set(String name, float value) {
170 return set(name, Float.valueOf(value), float.class);
171 }
172
173 /**
174 * As {@link #set(String, boolean)} but assigns a double value.
175 *
176 * @param name
177 * @param value
178 * @return This builder
179 */
180 public AnnotationBuilder<T> set(String name, double value) {
181 return set(name, Double.valueOf(value), double.class);
182 }
183
184 /**
185 * As {@link #set(String, boolean)} but assigns a String value. Throws a
186 * NullPointerException if value is null.
187 *
188 * @param name
189 * @param value
190 * @return This builder
191 */
192 public AnnotationBuilder<T> set(String name, String value) {
193 return set(name, value, String.class);
194 }
195
196 /**
197 * As {@link #set(String, boolean)} but assigns an Annotation instance to
198 * the value. A NullPointerException is thrown if value is null.
199 *
200 * @param name
201 * @param value
202 * @return This builder
203 */
204 public AnnotationBuilder<T> set(String name, Annotation value) {
205 return set(name, value, value.getClass());
206 }
207
208 /**
209 * As {@link #set(String, boolean)} but assigns a boolean[] value. A
210 * NullPointerException is thrown if the array is null.
211 *
212 * @param name
213 * @param value
214 * @return This builder
215 */
216 public AnnotationBuilder<T> set(String name, boolean[] value) {
217 return set(name, value, boolean[].class);
218 }
219
220 /**
221 * As {@link #set(String, boolean)} but assigns a byte[] value. A
222 * NullPointerException is thrown if the array is null.
223 *
224 * @param name
225 * @param value
226 * @return This builder
227 */
228 public AnnotationBuilder<T> set(String name, byte[] value) {
229 return set(name, value, byte[].class);
230 }
231
232 /**
233 * As {@link #set(String, boolean)} but assigns a short[] value. A
234 * NullPointerException is thrown if the array is null.
235 *
236 * @param name
237 * @param value
238 * @return This builder
239 */
240 public AnnotationBuilder<T> set(String name, short[] value) {
241 return set(name, value, short[].class);
242 }
243
244 /**
245 * As {@link #set(String, boolean)} but assigns a int[] value. A
246 * NullPointerException is thrown if the array is null.
247 *
248 * @param name
249 * @param value
250 * @return This builder
251 */
252 public AnnotationBuilder<T> set(String name, int[] value) {
253 return set(name, value, int[].class);
254 }
255
256 /**
257 * As {@link #set(String, boolean)} but assigns a long[] value. A
258 * NullPointerException is thrown if the array is null.
259 *
260 * @param name
261 * @param value
262 * @return This builder
263 */
264 public AnnotationBuilder<T> set(String name, long[] value) {
265 return set(name, value, long[].class);
266 }
267
268 /**
269 * As {@link #set(String, boolean)} but assigns a char[] value. A
270 * NullPointerException is thrown if the array is null.
271 *
272 * @param name
273 * @param value
274 * @return This builder
275 */
276 public AnnotationBuilder<T> set(String name, char[] value) {
277 return set(name, value, char[].class);
278 }
279
280 /**
281 * As {@link #set(String, boolean)} but assigns a float[] value. A
282 * NullPointerException is thrown if the array is null.
283 *
284 * @param name
285 * @param value
286 * @return This builder
287 */
288 public AnnotationBuilder<T> set(String name, float[] value) {
289 return set(name, value, float[].class);
290 }
291
292 /**
293 * As {@link #set(String, boolean)} but assigns a double[] value. A
294 * NullPointerException is thrown if the array is null.
295 *
296 * @param name
297 * @param value
298 * @return This builder
299 */
300 public AnnotationBuilder<T> set(String name, double[] value) {
301 return set(name, value, double[].class);
302 }
303
304 /**
305 * As {@link #set(String, boolean)} but assigns a String[] value. A
306 * NullPointerException is thrown if the array is null.
307 *
308 * @param name
309 * @param value
310 * @return This builder
311 */
312 public AnnotationBuilder<T> set(String name, String[] value) {
313 return set(name, value, String[].class);
314 }
315
316 /**
317 * As {@link #set(String, boolean)} but assigns an Annotation array to the
318 * value. The array must be a proper array over the parameterized type, and
319 * not a cast of Object[], etc. A NullPointerException is thrown if the
320 * array is null.
321 *
322 * @param name
323 * @param value
324 * @return This builder
325 */
326 public <A extends Annotation> AnnotationBuilder<T> set(String name, A[] value) {
327 return set(name, value, value.getClass());
328 }
329
330 /**
331 * As {@link #set(String, boolean)} but 'assigns an enum value'
332 * .A NullPointerException is thrown if value is null.
333 *
334 * @param name
335 * @param value
336 * @return This builder
337 */
338 public AnnotationBuilder<T> set(String name, Enum<? extends Enum> value) {
339 return set(name, value, value.getClass());
340 }
341
342 /**
343 * As {@link #set(String, Class)} but 'assigns an class value'
344 * .A NullPointerException is thrown if value is null.
345 *
346 * @param name
347 * @param value
348 * @return This builder
349 */
350 public AnnotationBuilder<T> set(String name, Class<? extends Class> value) {
351 return set(name, value, value.getClass());
352 }
353
354 /**
355 * Set the 'value' attribute to the given boolean value.
356 *
357 * @param value The boolean value
358 * @return This builder
359 */
360 public AnnotationBuilder<T> setValue(boolean value) {
361 return set("value", value);
362 }
363
364 /**
365 * Set the 'value' attribute to the given byte value.
366 *
367 * @param value The byte value
368 * @return This builder
369 */
370 public AnnotationBuilder<T> setValue(byte value) {
371 return set("value", value);
372 }
373
374 /**
375 * Set the 'value' attribute to the given short value.
376 *
377 * @param value The short value
378 * @return This builder
379 */
380 public AnnotationBuilder<T> setValue(short value) {
381 return set("value", value);
382 }
383
384 /**
385 * Set the 'value' attribute to the given int value.
386 *
387 * @param value The int value
388 * @return This builder
389 */
390 public AnnotationBuilder<T> setValue(int value) {
391 return set("value", value);
392 }
393
394 /**
395 * Set the 'value' attribute to the given long value.
396 *
397 * @param value The long value
398 * @return This builder
399 */
400 public AnnotationBuilder<T> setValue(long value) {
401 return set("value", value);
402 }
403
404 /**
405 * Set the 'value' attribute to the given double value.
406 *
407 * @param value The double value
408 * @return This builder
409 */
410 public AnnotationBuilder<T> setValue(double value) {
411 return set("value", value);
412 }
413
414 /**
415 * Set the 'value' attribute to the given float value.
416 *
417 * @param value The float value
418 * @return This builder
419 */
420 public AnnotationBuilder<T> setValue(float value) {
421 return set("value", value);
422 }
423
424 /**
425 * Set the 'value' attribute to the given char value.
426 *
427 * @param value The char value
428 * @return This builder
429 */
430 public AnnotationBuilder<T> setValue(char value) {
431 return set("value", value);
432 }
433
434 /**
435 * Set the 'value' attribute to the given String value.
436 *
437 * @param value The String value
438 * @return This builder
439 */
440 public AnnotationBuilder<T> setValue(String value) {
441 return set("value", value);
442 }
443
444 /**
445 * Set the 'value' attribute to the given annotation.
446 *
447 * @param value The annotation value
448 * @return This builder
449 */
450 public <A extends Annotation> AnnotationBuilder<T> setValue(A value) {
451 return set("value", value);
452 }
453
454 /**
455 * Set the 'value' attribute to the given boolean array.
456 *
457 * @param value The boolean array
458 * @return This builder
459 */
460 public AnnotationBuilder<T> setValue(boolean[] value) {
461 return set("value", value);
462 }
463
464 /**
465 * Set the 'value' attribute to the given byte array.
466 *
467 * @param value The byte array
468 * @return This builder
469 */
470 public AnnotationBuilder<T> setValue(byte[] value) {
471 return set("value", value);
472 }
473
474 /**
475 * Set the 'value' attribute to the given short array.
476 *
477 * @param value The short array
478 * @return This builder
479 */
480 public AnnotationBuilder<T> setValue(short[] value) {
481 return set("value", value);
482 }
483
484 /**
485 * Set the 'value' attribute to the given int array.
486 *
487 * @param value The int array
488 * @return This builder
489 */
490 public AnnotationBuilder<T> setValue(int[] value) {
491 return set("value", value);
492 }
493
494 /**
495 * Set the 'value' attribute to the given long array.
496 *
497 * @param value The long array
498 * @return This builder
499 */
500 public AnnotationBuilder<T> setValue(long[] value) {
501 return set("value", value);
502 }
503
504 /**
505 * Set the 'value' attribute to the given double array.
506 *
507 * @param value The double array
508 * @return This builder
509 */
510 public AnnotationBuilder<T> setValue(double[] value) {
511 return set("value", value);
512 }
513
514 /**
515 * Set the 'value' attribute to the given float array.
516 *
517 * @param value The float array
518 * @return This builder
519 */
520 public AnnotationBuilder<T> setValue(float[] value) {
521 return set("value", value);
522 }
523
524 /**
525 * Set the 'value' attribute to the given char array.
526 *
527 * @param value The char array
528 * @return This builder
529 */
530 public AnnotationBuilder<T> setValue(char[] value) {
531 return set("value", value);
532 }
533
534 /**
535 * Set the 'value' attribute to the given String array.
536 *
537 * @param value The String array
538 * @return This builder
539 */
540 public AnnotationBuilder<T> setValue(String[] value) {
541 return set("value", value);
542 }
543
544 /**
545 * Set the 'value' attribute to the given annotation array.
546 *
547 * @param value The annotation array
548 * @return This builder
549 */
550 public <A extends Annotation> AnnotationBuilder<T> setValue(A[] value) {
551 return set("value", value);
552 }
553
554 /**
555 * Set the 'value' attribute to the given enum.
556 *
557 * @param value The enum type annotation
558 * @return This builder
559 */
560 public AnnotationBuilder<T> setValue(Enum<? extends Enum> value) { return set("value", value);}
561
562 /**
563 * Set the 'value' attribute to the given class
564 *
565 * @param value The class type annotation
566 * @return This builder
567 */
568 public AnnotationBuilder<T> setValue(Class<? extends Class> value) { return set("value", value);}
569
570
571
572 private AnnotationBuilder<T> set(String name, Object value, Class<?> type) {
573 try {
574 Method attr = this.type.getMethod(name);
575 if (!attr.getReturnType().isAssignableFrom(type)) {
576 throw new IllegalArgumentException("Attribute named: " + name + " expects a type of " + attr.getReturnType() + ", but got " + type);
577 }
578
579 // if valid, save for later
580 attributes.put(name, AnnotationProxy.copyAnnotationValue(value));
581 return this;
582 } catch (SecurityException e) {
583 throw new RuntimeException(e);
584 } catch (NoSuchMethodException e) {
585 throw new IllegalArgumentException("Annotation type " + this.type + " does not have an attribute named: " + name, e);
586 }
587 }
588
589 /**
590 * Build an Annotation instance of type T that is configured to return the
591 * values assigned by the various set() methods for its defined attributes.
592 * If attributes have a default value and the value was not overridden by
593 * the builder's configuration, then the default will be returned by the
594 * annotation instance.
595 *
596 * @return An instance of T with the attribute values specified on this
597 * builder
598 * @throws IllegalStateException if there are attributes with no default
599 * that have not been assigned explicit values
600 */
601 public T build() {
602 for (Method attr: type.getDeclaredMethods()) {
603 if (attr.getDefaultValue() == null) {
604 // this is a required value, so we have to
605 // verify that its been assigned
606 if (!attributes.containsKey(attr.getName())) {
607 throw new IllegalStateException("No value assigned to required attribute: " + attr.getName());
608 }
609 }
610 }
611 return type.cast(Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { type },
612 new AnnotationProxy<T>(type, attributes)));
613 }
614 }