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 }