View Javadoc

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&lt;Named&gt;(Named.class)
39   *      .set(&quot;value&quot;, &quot;name&quot;)
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 }