* Removed all Id tags.
[cacao.git] / tests / regression / TestAnnotations.java
1 /* tests/regression/TestAnnotations.java - checks correct functionality of the
2    annotation API
3
4    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
5    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
6    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
7    TU Wien
8
9    This file is part of CACAO.
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2, or (at
14    your option) any later version.
15
16    This program is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24    02110-1301, USA.
25
26    Contact: cacao@cacaojvm.org
27
28    Authors: Mathias Panzenböck
29
30 */
31
32 import java.lang.annotation.Annotation;
33 import java.lang.annotation.Inherited;
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.lang.reflect.AnnotatedElement;
37 import java.lang.reflect.Array;
38 import java.lang.reflect.Constructor;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Field;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.Comparator;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.HashMap;
49 import java.util.SortedSet;
50 import java.util.TreeMap;
51 import java.util.LinkedHashMap;
52 import java.util.Set;
53 import java.util.HashSet;
54 import java.util.TreeSet;
55 import java.util.LinkedHashSet;
56
57 /* ********* helper classes for the tests *************************************/
58 enum IndexingType {
59         HASH,
60         LINKED_HASH,
61         TREE
62 }
63
64 class MapFactory {
65         private IndexingType indexingType;
66         
67         public MapFactory(IndexingType indexingType) {
68                 this.indexingType = indexingType;
69         }
70         
71         public <K, V> Map<K, V> createMap() {
72                 switch (indexingType) {
73                 case HASH:        return new HashMap<K, V>();
74                 case LINKED_HASH: return new LinkedHashMap<K, V>(); 
75                 case TREE:        return new TreeMap<K, V>();
76                 default:
77                         throw new IllegalArgumentException(
78                                         "Illegal indexing type: " + indexingType);
79                 }
80         }
81
82         public <K, V> Map<K, V> createMap(Map<? extends K,? extends V> map) {
83                 switch (indexingType) {
84                 case HASH:        return new HashMap<K, V>(map);
85                 case LINKED_HASH: return new LinkedHashMap<K, V>(map);
86                 case TREE:        return new TreeMap<K, V>(map);
87                 default:
88                         throw new IllegalArgumentException(
89                                         "Illegal indexing type: " + indexingType);
90                 }
91         }
92 }
93
94 class SetFactory {
95         private IndexingType indexingType;
96         
97         public SetFactory(IndexingType indexingType) {
98                 this.indexingType = indexingType;
99         }
100         
101         public <E> Set<E> createSet() {
102                 switch (indexingType) {
103                 case HASH:        return new HashSet<E>();
104                 case TREE:        return new TreeSet<E>();
105                 case LINKED_HASH: return new LinkedHashSet<E>();
106                 default:
107                         throw new IllegalArgumentException(
108                                         "Illegal indexing type: " + indexingType);
109                 }
110         }
111         
112         public <E> Set<E> createSet(E[] keys) {
113                 Set<E> set = createSet();
114                 
115                 for (E key : keys) {
116                         set.add(key);
117                 }
118                 
119                 return set;
120         }
121         
122         public <E> Set<E> createSet(Collection<? extends E> collection) {
123                 switch (indexingType) {
124                 case HASH:        return new HashSet<E>(collection);
125                 case TREE:        return new TreeSet<E>(collection);
126                 case LINKED_HASH: return new LinkedHashSet<E>(collection);
127                 default:
128                         throw new IllegalArgumentException(
129                                         "Illegal indexing type: " + indexingType);
130                 }
131         }
132 }
133
134 class TestHelper {
135         private static MapFactory mapFactory = new MapFactory(IndexingType.HASH);
136         private static SetFactory setFactory = new SetFactory(IndexingType.HASH);
137         
138         private static long testCount = 0;
139         private static long failCount = 0;
140
141         public static MapFactory getMapFactory() {
142                 return mapFactory;
143         }
144         
145         public static SetFactory getSetFactory() {
146                 return setFactory;
147         }
148         
149         public static void clear() {
150                 testCount = 0;
151                 failCount = 0;
152         }
153
154         public static long getTestCount() {
155                 return testCount;
156         }
157
158         public static long getFailCount() {
159                 return failCount;
160         }
161
162         public static void printStatistics() {
163                 System.out.printf("         passed: %8d\n", testCount - failCount);
164                 System.out.printf("         failed: %8d\n", failCount);
165                 System.out.printf("         ----------------\n");
166                 System.out.printf("         sum:    %8d\n", testCount);
167         }
168
169         public static void log() {
170                 System.out.println();
171         }
172
173         public static void log(String msg) {
174                 System.out.println(msg);
175         }
176
177         public static void log(String fmt, Object... args) {
178                 System.out.printf(fmt + "\n", args);
179         }
180
181         public static boolean ok(boolean test, String msg) {
182                 return ok(test, "%s", msg);
183         }
184
185         public static boolean ok(boolean test, String fmt, Object... args) {
186                 ++testCount;
187
188                 if (test) {
189                         System.out.printf("[  OK  ] " + fmt + "\n", args);
190                         return true;
191                 } else {
192                         ++failCount;
193                         System.out.printf("[ FAIL ] " + fmt + "\n", args);
194                         return false;
195                 }
196         }
197
198         public static boolean okx(boolean test, String what, String fmt,
199                         String errfmt, Object[] args, Object... errargs) {
200                 if (!test)
201                         return ok(test, fmt + ": %s\n         error: " + errfmt, concat(
202                                         concat(args, what), errargs));
203                 else
204                         return ok(test, fmt + ": %s", concat(args, what));
205         }
206
207         /* helper methods: */
208         @SuppressWarnings("unchecked")
209         public static <T> T[] concat(T[] firstArray, T... moreElements) {
210                 return concat2(firstArray, moreElements);
211         }
212
213         @SuppressWarnings("unchecked")
214         public static <T> T[] concat2(T[] firstArray, T[]... moreArrays) {
215                 int length = firstArray.length;
216
217                 for (T[] array : moreArrays) {
218                         length += array.length;
219                 }
220
221                 T[] result = (T[]) Array.newInstance(firstArray.getClass()
222                                 .getComponentType(), length);
223
224                 System.arraycopy(firstArray, 0, result, 0, firstArray.length);
225
226                 int pos = firstArray.length;
227                 for (T[] array : moreArrays) {
228                         System.arraycopy(array, 0, result, pos, array.length);
229                         pos += array.length;
230                 }
231
232                 return result;
233         }
234
235         public static <T> String str(T object) {
236                 /* null */
237                 if (object == null) {
238                         return "null";
239                 }
240                 /* array */
241                 else if (object.getClass().isArray()) {
242                         StringBuilder buf = new StringBuilder();
243                         int length = Array.getLength(object);
244
245                         buf.append('{');
246
247                         if (length > 0) {
248                                 buf.append(str(Array.get(object, 0)));
249
250                                 for (int i = 1; i < length; ++i) {
251                                         buf.append(", ");
252                                         buf.append(str(Array.get(object, i)));
253                                 }
254                         }
255
256                         buf.append('}');
257
258                         return buf.toString();
259                 }
260                 /* escape String */
261                 else if (object instanceof String) {
262                         String s = object.toString();
263                         StringBuilder buf = new StringBuilder();
264
265                         buf.append('"');
266
267                         for (int i = 0; i < s.length(); ++i) {
268                                 char ch = s.charAt(i);
269
270                                 switch (ch) {
271                                 case '"':
272                                         buf.append("\\\"");
273                                         break;
274                                 case '\\':
275                                         buf.append("\\\\");
276                                         break;
277                                 case '\b':
278                                         buf.append("\\b");
279                                         break;
280                                 case '\f':
281                                         buf.append("\\f");
282                                         break;
283                                 case '\t':
284                                         buf.append("\\t");
285                                         break;
286                                 case '\n':
287                                         buf.append("\\n");
288                                         break;
289                                 case '\r':
290                                         buf.append("\\r");
291                                         break;
292                                 default:
293                                         buf.append(ch);
294                                 }
295                         }
296
297                         buf.append('"');
298                         return buf.toString();
299                 }
300                 /* escape Character */
301                 else if (object instanceof Character) {
302                         switch ((Character) object) {
303                         case '\'':
304                                 return "'\\''";
305                         case '\\':
306                                 return "'\\\\'";
307                         case '\b':
308                                 return "'\\b'";
309                         case '\f':
310                                 return "'\\f'";
311                         case '\t':
312                                 return "'\\t'";
313                         case '\n':
314                                 return "'\\n'";
315                         case '\r':
316                                 return "'\\r'";
317                         default:
318                                 return "'" + object + "'";
319                         }
320                 }
321                 /* Class */
322                 else if (object instanceof Class) {
323                         return ((Class<?>) object).getName();
324                 }
325
326                 return object.toString();
327         }
328         
329         public static <E extends Comparable<? super E>> Collection<E> sorted(
330                         Collection<E> collection) {
331                 if (collection instanceof SortedSet) {
332                         return collection;
333                 }
334                 else {
335                         List<E> reply = new ArrayList<E>(collection);
336                         Collections.sort(reply);
337                         
338                         return reply;
339                 }
340         }
341
342         public static <E> Collection<E> sorted(
343                         Collection<E> collection,
344                         Comparator<? super E> comparator) {
345                 if (collection instanceof SortedSet) {
346                         return collection;
347                 }
348                 else {
349                         List<E> reply = new ArrayList<E>(collection);
350                         Collections.sort(reply, comparator);
351                         
352                         return reply;
353                 }
354         }
355
356         public static <E extends Comparable<? super E>> E[] sorted(E[] values) {
357                 Arrays.sort(values);
358                 return values;
359         }
360         
361         public static <E> E[] sorted(
362                         E[] values,
363                         Comparator<? super E> comparator) {
364                 Arrays.sort(values, comparator);
365                 return values;
366         }
367 }
368
369 class Entry implements Map.Entry<String, Object> {
370         private String key;
371         private Object val;
372
373         public Entry(String key, Object value) {
374                 this.key = key;
375                 this.val = value;
376         }
377
378         public String getKey() {
379                 return key;
380         }
381
382         public Object getValue() {
383                 return val;
384         }
385
386         public Object setValue(Object value) {
387                 Object oldval = val;
388                 val = value;
389                 return oldval;
390         }
391
392         public int hashCode() {
393                 return (key != null ? key.hashCode() << 8 : 0)
394                                 ^ (val != null ? val.hashCode() : 0);
395         }
396
397         public boolean equals(Object other) {
398                 if (other != null && other instanceof Entry) {
399                         Entry otherEntry = (Entry) other;
400
401                         return (key == null ? otherEntry.key == null :
402                                 key.equals(otherEntry.key))
403                                         && (val == null ? otherEntry.val == null :
404                                                 val.equals(otherEntry.val));
405                 }
406
407                 return false;
408         }
409 }
410
411 class AnnotationTester implements Comparable<AnnotationTester> {
412         private Class<? extends Annotation> annotationType;
413
414         private Map<String, Object> values =
415                 TestHelper.getMapFactory().<String, Object>createMap();
416
417         public AnnotationTester(Class<? extends Annotation> annotationType,
418                         Map<String, Object> values) {
419                 this.annotationType = annotationType;
420                 this.values.putAll(values);
421
422                 checkValues();
423         }
424
425         public AnnotationTester(Class<? extends Annotation> annotationType) {
426                 this.annotationType = annotationType;
427
428                 checkValues();
429         }
430
431         public AnnotationTester(Class<? extends Annotation> annotationType,
432                         Map.Entry<String, Object>... values) {
433                 this.annotationType = annotationType;
434
435                 for (Map.Entry<String, Object> value : values) {
436                         if (this.values.put(value.getKey(), value.getValue()) != null)
437                                 throw new IllegalArgumentException(
438                                                 "duplicated key: " + TestHelper.str(value.getKey()));
439                 }
440
441                 checkValues();
442         }
443
444         public Class<? extends Annotation> annotationType() {
445                 return annotationType;
446         }
447
448         private void checkValues() {
449                 for (String methodName : values.keySet()) {
450                         try {
451                                 annotationType.getDeclaredMethod(methodName);
452                         } catch (NoSuchMethodException e) {
453                                 throw new IllegalArgumentException(
454                                                 "annotation " + annotationType.getName() +
455                                                 " has no method of name " + methodName + "()", e);
456                         }
457                 }
458
459                 for (Method method : annotationType.getDeclaredMethods()) {
460                         Object value = values.get(method.getName());
461                         Class<?> returnType = method.getReturnType();
462                         Class<?> valueType = value.getClass();
463
464                         if (value == null) {
465                                 throw new IllegalArgumentException(
466                                                 "annotation method of name " + method.getName() +
467                                                 "() needs an expected value");
468                         } else if (value instanceof AnnotationTester) {
469                                 AnnotationTester tester = (AnnotationTester) value;
470
471                                 if (!tester.annotationType().equals(returnType)) {
472                                         throw new IllegalArgumentException(
473                                                         "illegal value type for annotation method " +
474                                                         method.getName() + "()");
475                                 }
476                         } else if (!returnType.isInstance(value)) {
477                                 if (returnType.isArray()
478                                                 && returnType.getComponentType().isAnnotation()) {
479                                         if (!valueType.isArray()
480                                                         || !isSubclassOf(valueType.getComponentType(),
481                                                                         AnnotationTester.class)) {
482                                                 throw new IllegalArgumentException(
483                                                                 "illegal value type for annotation method " +
484                                                                 method.getName() + "()");
485                                         }
486
487                                         for (AnnotationTester tester : (AnnotationTester[]) value) {
488                                                 if (!tester.annotationType().equals(
489                                                                 returnType.getComponentType())) {
490                                                         throw new IllegalArgumentException(
491                                                                         "illegal value type for annotation method " +
492                                                                         method.getName() + "()");
493                                                 }
494                                         }
495                                 } else if (!returnType.isPrimitive()
496                                                 || !valueType.equals(getBoxingType(returnType))) {
497                                         throw new IllegalArgumentException(
498                                                         "illegal value type for annotation method " +
499                                                         method.getName() + "()");
500                                 }
501                         }
502                 }
503         }
504
505         public static boolean isSubclassOf(Class<?> subClass, Class<?> superClass) {
506                 do {
507                         if (subClass.equals(superClass)) {
508                                 return true;
509                         }
510                         subClass = subClass.getSuperclass();
511                 } while (subClass != null);
512
513                 return false;
514         }
515
516         private static Map<Class<?>, Class<?>> boxingMap =
517                 TestHelper.getMapFactory().<Class<?>, Class<?>>createMap();
518
519         static {
520                 boxingMap.put(byte.class,   Byte.class);
521                 boxingMap.put(char.class,   Character.class);
522                 boxingMap.put(short.class,  Short.class);
523                 boxingMap.put(long.class,   Long.class);
524                 boxingMap.put(int.class,    Integer.class);
525                 boxingMap.put(float.class,  Float.class);
526                 boxingMap.put(double.class, Double.class);
527         }
528
529         public static Class<?> getBoxingType(Class<?> primitiveType) {
530                 Class<?> type = boxingMap.get(primitiveType);
531
532                 if (type == null) {
533                         throw new IllegalArgumentException(
534                                         "illegal type for boxing: "     + primitiveType.getName());
535                 }
536
537                 return type;
538         }
539
540         public int hashCode() {
541                 return (annotationType.hashCode() << 8) ^ values.hashCode();
542         }
543
544         public boolean equals(Object other) {
545                 if (other != null) {
546                         if (other instanceof AnnotationTester) {
547                                 AnnotationTester otherAnnotationTester =
548                                         (AnnotationTester) other;
549
550                                 if (!otherAnnotationTester.annotationType.equals(annotationType) ||
551                                                 otherAnnotationTester.values.size() != values.size())
552                                         return false;
553
554                                 for (Map.Entry<String, Object> entry : values.entrySet()) {
555                                         if (!otherAnnotationTester.values.containsKey(entry.getKey()) ||
556                                                         !otherAnnotationTester.values.get(
557                                                                         entry.getKey()).equals(entry.getValue()))
558                                                 return false;
559                                 }
560
561                                 return true;
562                         } else if (other instanceof Annotation) {
563                                 Annotation anno = (Annotation) other;
564                                 Method[] annotationFields = anno.annotationType()
565                                                 .getDeclaredMethods();
566
567                                 if (!annotationType.equals(anno.annotationType())
568                                                 || annotationFields.length != values.size())
569                                         return false;
570
571                                 for (Method method : annotationFields) {
572                                         if (!values.get(method.getName()).equals(
573                                                         method.getDefaultValue()))
574                                                 return false;
575                                 }
576
577                                 return true;
578                         }
579                 }
580                 return false;
581         }
582         
583         public String toString() {
584                 StringBuilder buf = new StringBuilder();
585                 
586                 buf.append('@');
587                 buf.append(annotationType.getName());
588                 buf.append('(');
589                 
590                 int i = 0;
591                 for (Map.Entry<String, Object> entry : values.entrySet()) {
592                         buf.append(entry.getKey());
593                         buf.append('=');
594                         buf.append(TestHelper.str(entry.getValue()));
595                         
596                         ++ i;
597                         if (i < values.size()) {
598                                 buf.append(", ");
599                         }
600                 }
601                 buf.append(')');
602                 
603                 return buf.toString();
604         }
605
606         private final static Object[] EMPTY_OBJECT_ARRAY = new Object[] {};
607
608         protected boolean ok(boolean test, String what, String errfmt,
609                         Object... errargs) {
610                 return TestHelper.okx(test, what, annotationType.getName(), errfmt,
611                                 EMPTY_OBJECT_ARRAY, errargs);
612         }
613
614         public boolean test(Annotation annotation) throws SecurityException,
615                         NoSuchMethodException {
616                 boolean ok = true;
617                 Method[] declaredMedthods = annotation.annotationType()
618                                 .getDeclaredMethods();
619
620                 TestHelper.log(" * Testing %s", annotationType.getName());
621                 
622                 ok = ok(annotationType.equals(annotation.annotationType()),
623                                 "test annotationType", "expected %s but got %s",
624                                 annotationType, annotation.annotationType());
625
626                 if (ok) {
627                         ok = ok(declaredMedthods.length == values.size(),
628                                         "test annotation methods count", "expected %d but got %d",
629                                         values.size(), declaredMedthods.length)
630                                         && ok;
631                         
632                         for (String methodName : TestHelper.sorted(values.keySet())) {
633                                 Object    expected = values.get(methodName);
634                                 Object    got = null;
635                                 Exception ex  = null;
636
637                                 try {
638                                         got = annotation.getClass().getMethod(methodName).invoke(
639                                                         annotation);
640                                 } catch (Exception e) {
641                                         ex = e;
642                                 }
643
644                                 ok = ok(ex == null,
645                                                 "test invocation of the annotation method " +
646                                                 methodName + "()",
647                                                 "exception occured while invokation: %s",
648                                                 ex != null ? ex.getMessage() : "")
649                                                 && ok;
650
651                                 if (ex != null) {
652                                         ex.printStackTrace();
653                                 } else {
654                                         ok = ok(got != null, "test return value of " + methodName +
655                                                         "() != null", "got null!")
656                                                         && ok;
657
658                                         ok = ok(equals(got, expected), "test return value of "
659                                                         + methodName + "()", "expected %s (type: %s) but"
660                                                         + " got %s (type: %s)", TestHelper.str(got), got
661                                                         .getClass().getName(), TestHelper.str(expected),
662                                                         expected.getClass().getName())
663                                                         && ok;
664                                 }
665                         }
666                 }
667
668                 return ok;
669         }
670
671         public static boolean equals(Object a, Object b) {
672                 if (a == null) {
673                         return b == null;
674                 }
675                 else if (a instanceof Annotation && b instanceof AnnotationTester) {
676                         return equals((Annotation) a, (AnnotationTester) b);
677                 }
678                 else if (b instanceof Annotation && a instanceof AnnotationTester) {
679                         return equals((Annotation) b, (AnnotationTester) a);
680                 }
681                 else if (a.getClass().isArray()) {
682                         if (!b.getClass().isArray()) {
683                                 return false;
684                         }
685
686                         int alen = Array.getLength(a);
687                         int blen = Array.getLength(b);
688
689                         if (alen != blen) {
690                                 return false;
691                         }
692
693                         for (int i = 0; i < alen; ++i) {
694                                 if (!equals(Array.get(a, i), Array.get(b, i))) {
695                                         return false;
696                                 }
697                         }
698
699                         return true;
700                 }
701                 else {
702                         return a.equals(b);
703                 }
704         }
705
706         public static boolean equals(Annotation annoation, AnnotationTester tester) {
707                 if (!tester.annotationType().equals(annoation.annotationType())) {
708                         return false;
709                 }
710
711                 try {
712                         for (Map.Entry<String, Object> bEntry : tester.values.entrySet()) {
713                                 Object aValue = annoation.getClass().getMethod(bEntry.getKey())
714                                                 .invoke(annoation);
715
716                                 if (!equals(bEntry.getValue(), aValue)) {
717                                         return false;
718                                 }
719                         }
720                 } catch (Exception e) {
721                         // TODO: better error handling?
722                         e.printStackTrace();
723                         return false;
724                 }
725
726                 return true;
727         }
728
729         public int compareTo(AnnotationTester other) {
730                 return annotationType.getName().compareTo(
731                                 other.annotationType().getName());
732         }
733 }
734
735 abstract class AnnotatedElementAnnotationTester<T extends AnnotatedElement>
736                 implements Comparable<AnnotatedElementAnnotationTester<? extends AnnotatedElement>> {
737         protected ClassAnnotationTester declaringClass;
738         private T                       element;
739         private String                  name;
740         
741         private Map<Class<? extends Annotation>, AnnotationTester> annotations =
742                 TestHelper.getMapFactory().
743                 <Class<? extends Annotation>, AnnotationTester>createMap();
744         
745         private Map<Class<? extends Annotation>, AnnotationTester> declaredAnnotations =
746                 TestHelper.getMapFactory().
747                 <Class<? extends Annotation>, AnnotationTester>createMap();
748
749         protected final static Object[] EMPTY_OBJECT_ARRAY = new Object[] {};
750
751         public AnnotatedElementAnnotationTester(
752                         ClassAnnotationTester clazz,
753                         T                     element,
754                         String                name) {
755                 this.declaringClass = clazz;
756                 this.element        = element;
757                 this.name           = name;
758         }
759         
760         public T annotatedElement() {
761                 return element;
762         }
763         
764         public String getName() {
765                 return name;
766         }
767         
768         public int compareTo(
769                         AnnotatedElementAnnotationTester<? extends AnnotatedElement> other) {
770                 return name.compareTo(other.getName());
771         }
772         
773         private static Comparator<Annotation> annotationComparator =
774                 new Comparator<Annotation>() {
775                         public int compare(Annotation o1, Annotation o2) {
776                                 return o1.annotationType().getName().compareTo(
777                                                 o2.annotationType().getName());
778                         }
779         };
780         
781         protected static Annotation[] sorted(Annotation[] annotations) {
782                 return TestHelper.sorted(annotations, annotationComparator);
783         }
784
785         protected boolean ok(
786                         boolean test, String what,
787                         String errfmt, Object... errargs) {
788                 return TestHelper.okx(test, what, name, errfmt,
789                                 EMPTY_OBJECT_ARRAY, errargs);
790         }
791         
792         protected void logName() {
793                 TestHelper.log("-- Testing %s --", getName());
794         }
795         
796         protected void logHeader(String fmt, Object... args) {
797                 TestHelper.log("-- " + getName() + ": Testing " + fmt + " --", args);
798         }
799
800         protected void log() {
801                 TestHelper.log();
802         }
803
804         public void putInheritedAnnotation(
805                         Class<? extends Annotation> annotationType,
806                         Map.Entry<String, Object>... values) {
807                 if (annotations.containsKey(annotationType))
808                         throw new IllegalArgumentException(
809                                         "Annotation already exists: " + annotationType.getName());
810
811                 annotations.put(annotationType,
812                                 new AnnotationTester(annotationType, values));
813         }
814
815         public void putInheritedAnnotation(
816                         Class<? extends Annotation> annotationType,
817                         Map<String, Object> values) {
818                 if (annotations.containsKey(annotationType))
819                         throw new IllegalArgumentException(
820                                         "Annotation already exists: " + annotationType.getName());
821
822                 annotations.put(annotationType,
823                                 new AnnotationTester(annotationType, values));
824         }
825
826         public void putDeclaredAnnotation(
827                         Class<? extends Annotation> annotationType,
828                         Map.Entry<String, Object>... values) {
829                 if (annotations.containsKey(annotationType))
830                         throw new IllegalArgumentException(
831                                         "Annotation already exists: " + annotationType.getName());
832
833                 AnnotationTester tester = new AnnotationTester(annotationType, values);
834
835                 annotations.put(annotationType, tester);
836                 declaredAnnotations.put(annotationType, tester);
837         }
838
839         public void putDeclaredAnnotation(
840                         Class<? extends Annotation> annotationType,
841                         Map<String, Object> values) {
842                 if (annotations.containsKey(annotationType))
843                         throw new IllegalArgumentException(
844                                         "Annotation already exists: " + annotationType.getName());
845
846                 AnnotationTester tester = new AnnotationTester(annotationType, values);
847
848                 annotations.put(annotationType, tester);
849                 declaredAnnotations.put(annotationType, tester);
850         }
851
852         public boolean test() throws SecurityException, NoSuchMethodException {
853                 boolean ok;
854                 
855                 logName();
856                 
857                 ok = testGetDeclaredAnnotations();
858                 ok = testGetAnnotations()      && ok;
859                 ok = testGetAnnotation()       && ok;
860                 ok = testIsAnnotationPresent() && ok;
861
862                 return ok;
863         }
864
865
866         private boolean testGetDeclaredAnnotations() throws SecurityException,
867                         NoSuchMethodException {
868                 Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
869                 boolean ok = true;
870                 Set<Class<? extends Annotation>> annoTypes =
871                         TestHelper.getSetFactory().<Class<? extends Annotation>>createSet();
872
873                 logHeader("getDeclaredAnnotations()");
874                 
875                 ok = ok(this.declaredAnnotations.size() == declaredAnnotations.length,
876                                 "test declared annotations count", "expected %d but got %d",
877                                 this.declaredAnnotations.size(), declaredAnnotations.length)
878                                 && ok;
879
880                 for (Annotation anno : sorted(declaredAnnotations)) {
881                         AnnotationTester tester = this.annotations.get(
882                                         anno.annotationType());
883
884                         ok = ok(!annoTypes.contains(anno.annotationType()),
885                                         "test unique occurrence of the annotation type " +
886                                         anno.annotationType().getName(),
887                                         "duplicated annotation!") && ok;
888
889                         annoTypes.add(anno.annotationType());
890
891                         ok = ok(tester != null, "test if annotation " +
892                                         anno.annotationType().getName() + " should be there",
893                                         "wrong annotation") && ok;
894
895                         if (tester != null) {
896                                 ok = tester.test(anno) && ok;
897                         }
898                 }
899
900                 return ok;
901         }
902         
903         private boolean testGetAnnotations() throws SecurityException,
904                         NoSuchMethodException {
905                 Annotation[] annotations = element.getAnnotations();
906                 boolean ok = true;
907                 Set<Class<? extends Annotation>> annoTypes =
908                         TestHelper.getSetFactory().<Class<? extends Annotation>>createSet();
909
910                 logHeader("getAnnotations()");
911                 
912                 ok = ok(this.annotations.size() == annotations.length,
913                                 "test annotations count", "expected %d but got %d",
914                                 this.annotations.size(), annotations.length)
915                                 && ok;
916
917                 for (Annotation anno : sorted(annotations)) {
918                         AnnotationTester tester = this.annotations.get(anno
919                                         .annotationType());
920
921                         ok = ok(!annoTypes.contains(anno.annotationType()),
922                                         "test unique occurrence of the annotation type " +
923                                         anno.annotationType().getName(),
924                                         "duplicated annotation!")
925                                         && ok;
926
927                         annoTypes.add(anno.annotationType());
928
929                         ok = ok(tester != null, "test if annotation " +
930                                         anno.annotationType().getName() + " should be there",
931                                         "wrong annotation")
932                                         && ok;
933
934                         if (tester != null) {
935                                 ok = tester.test(anno) && ok;
936                         }
937                 }
938
939                 return ok;
940         }
941
942         private boolean testGetAnnotation() throws SecurityException,
943                         NoSuchMethodException {
944                 boolean ok = true;
945
946                 logHeader("getAnnotation(Class<? extends Annotation>)");
947                 
948                 for (AnnotationTester tester : TestHelper.sorted(annotations.values())) {
949                         Class<? extends Annotation> annotationType = tester
950                                         .annotationType();
951                         Annotation anno = element.getAnnotation(annotationType);
952
953                         ok = ok(anno != null, "try to get required annotation " +
954                                         annotationType.getName() +
955                                         " using getAnnotation(Class<? extends Annotation>)",
956                                         "annotation dose not exist")
957                                         && ok;
958
959                         if (anno != null) {
960                                 ok = tester.test(anno) && ok;
961                         }
962                 }
963
964                 return ok;
965         }
966
967         private boolean testIsAnnotationPresent() {
968                 boolean ok = true;
969
970                 logHeader("isAnnotationPresent(Class<? extends Annotation>)");
971                 
972                 for (AnnotationTester tester : TestHelper.sorted(annotations.values())) {
973                         Class<? extends Annotation> annotationType =
974                                 tester.annotationType();
975
976                         ok = ok(element.isAnnotationPresent(annotationType),
977                                         "test if annotation " + annotationType.getName() +
978                                         " is present using isAnnotationPresent()",
979                                         "annotation dose not exist")
980                                         && ok;
981                 }
982
983                 return ok;
984         }
985 }
986
987 class FieldAnnotationTester extends AnnotatedElementAnnotationTester<Field> {
988         public FieldAnnotationTester(ClassAnnotationTester clazz, Field field) {
989                 super(clazz, field, field.getDeclaringClass().getName() + "." +
990                                 field.getName());
991         }
992 }
993
994 abstract class AbstractMethodAnnotationTester<T extends AnnotatedElement>
995                 extends AnnotatedElementAnnotationTester<T> {
996         private Map<Class<? extends Annotation>, AnnotationTester>[] parameterAnnotations;
997
998         @SuppressWarnings("unchecked")
999         public AbstractMethodAnnotationTester(ClassAnnotationTester clazz,
1000                         T element, String name, Class<?>[] parameterTypes) {
1001                 super(clazz, element,
1002                                 methodName(clazz.annotatedElement(), name, parameterTypes));
1003
1004                 parameterAnnotations = new Map[parameterTypes.length];
1005
1006                 MapFactory mapFactory = TestHelper.getMapFactory();
1007                 
1008                 for (int i = 0; i < parameterTypes.length; ++i) {
1009                         parameterAnnotations[i] = mapFactory.
1010                                 <Class<? extends Annotation>, AnnotationTester>createMap();
1011                 }
1012         }
1013
1014         private static String methodName(Class<?> declaringClass, String name,
1015                         Class<?>[] parameterTypes) {
1016                 StringBuilder buf = new StringBuilder(128);
1017
1018                 buf.append(declaringClass.getName());
1019                 buf.append('.');
1020                 buf.append(name);
1021                 buf.append('(');
1022
1023                 if (parameterTypes.length > 0) {
1024                         buf.append(parameterTypes[0].getName());
1025
1026                         for (int i = 1; i < parameterTypes.length; ++i) {
1027                                 buf.append(',');
1028                                 buf.append(parameterTypes[i].getName());
1029                         }
1030                 }
1031
1032                 buf.append(')');
1033
1034                 return buf.toString();
1035         }
1036
1037         abstract protected Annotation[][] getParameterAnnotations();
1038
1039         public void putParameterAnnotation(int index,
1040                         Class<? extends Annotation> annotationType,
1041                         Map.Entry<String, Object>... values) {
1042                 if (parameterAnnotations[index].containsKey(annotationType))
1043                         throw new IllegalArgumentException(
1044                                         "Annotation already exists: " + annotationType.getName());
1045
1046                 parameterAnnotations[index].put(
1047                                 annotationType,
1048                                 new AnnotationTester(annotationType, values));
1049         }
1050
1051         public void putParameterAnnotation(int index,
1052                         Class<? extends Annotation> annotationType,
1053                         Map<String, Object> values) {
1054                 if (parameterAnnotations[index].containsKey(annotationType))
1055                         throw new IllegalArgumentException(
1056                                         "Annotation already exists: " + annotationType.getName());
1057
1058                 parameterAnnotations[index].put(
1059                                 annotationType,
1060                                 new AnnotationTester(annotationType, values));
1061         }
1062
1063         public boolean test() throws SecurityException, NoSuchMethodException {
1064                 boolean ok = super.test();
1065
1066                 ok = testParameterAnnotations() && ok;
1067
1068                 return ok;
1069         }
1070
1071         private boolean testParameterAnnotations() throws SecurityException,
1072                         NoSuchMethodException {
1073                 boolean ok = true;
1074                 Annotation[][] parameterAnnotations = getParameterAnnotations();
1075
1076                 logHeader("getParameterAnnotations()");
1077                 
1078                 ok = ok(
1079                                 this.parameterAnnotations.length == parameterAnnotations.length,
1080                                 "test parameter count", "expected %d but got %d",
1081                                 this.parameterAnnotations.length, parameterAnnotations.length)
1082                                 && ok;
1083
1084                 if (this.parameterAnnotations.length == parameterAnnotations.length) {
1085                         for (int i = 0; i < parameterAnnotations.length; ++i) {
1086                                 Set<Class<? extends Annotation>> annoTypes =
1087                                         TestHelper.getSetFactory().
1088                                         <Class<? extends Annotation>>createSet();
1089
1090                                 ok = ok(
1091                                                 this.parameterAnnotations[i].size() == parameterAnnotations[i].length,
1092                                                 "test parameter annotations count for parameter " + i,
1093                                                 "expected %d but got %d",
1094                                                 Integer.valueOf(this.parameterAnnotations.length),
1095                                                 Integer.valueOf(parameterAnnotations.length))
1096                                                 && ok;
1097
1098                                 for (Annotation anno : sorted(parameterAnnotations[i])) {
1099                                         AnnotationTester tester =
1100                                                 this.parameterAnnotations[i].get(anno.annotationType());
1101
1102                                         ok = ok(!annoTypes.contains(anno.annotationType()),
1103                                                         "test unique occurrence of the annotation type " +
1104                                                         anno.annotationType().getName(),
1105                                                         "duplicated annotation!")
1106                                                         && ok;
1107
1108                                         annoTypes.add(anno.annotationType());
1109
1110                                         ok = ok(tester != null, "test if annotation of type " +
1111                                                         anno.annotationType().getName() +
1112                                                         " should be defined for parameter " + i,
1113                                                         "no, it shouldn't be!")
1114                                                         && ok;
1115
1116                                         if (tester != null) {
1117                                                 ok = tester.test(anno) && ok;
1118                                         }
1119                                 }
1120                         }
1121                 }
1122
1123                 return ok;
1124         }
1125 }
1126
1127 class MethodAnnotationTester extends AbstractMethodAnnotationTester<Method> {
1128         private Object defaultValue = null;
1129
1130         public MethodAnnotationTester(ClassAnnotationTester clazz, Method method) {
1131                 super(clazz, method, method.getName(), method.getParameterTypes());
1132         }
1133
1134         public MethodAnnotationTester(ClassAnnotationTester clazz, Method method,
1135                         Object defaultValue) {
1136                 this(clazz, method);
1137                 setDefaultValue(defaultValue);
1138         }
1139
1140         public void setDefaultValue(Object value) {
1141                 if (value != null && !declaringClass.isAnnotation())
1142                         throw new IllegalArgumentException(
1143                                         "cannot set annotation default value of a method " +
1144                                         "of a non-annotation class.");
1145
1146                 defaultValue = value;
1147         }
1148
1149         public Object getDefaultValue() {
1150                 return defaultValue;
1151         }
1152
1153         public boolean test() throws SecurityException, NoSuchMethodException {
1154                 boolean ok = testGetDefaultValue();
1155
1156                 return super.test() && ok;
1157         }
1158
1159         private boolean testGetDefaultValue() {
1160                 boolean ok = true;
1161                 Object defaultValue = annotatedElement().getDefaultValue();
1162                 
1163                 logHeader("getDefaultValue()");
1164
1165                 if (this.defaultValue == null) {
1166                         ok = ok(defaultValue == null, "test for annotation " +
1167                                         "default value", "there is one, but it should NOT be one!")
1168                                         && ok;
1169                 } else {
1170                         ok = ok(defaultValue != null, "test for annotation " +
1171                                         "default value", "there is NONE, but it should be one!")
1172                                         && ok;
1173
1174                         ok = ok(AnnotationTester.equals(this.defaultValue, defaultValue),
1175                                         "test default value", "expected %s but got %s",
1176                                         this.defaultValue, defaultValue)
1177                                         && ok;
1178                 }
1179
1180                 return ok;
1181         }
1182
1183         protected Annotation[][] getParameterAnnotations() {
1184                 return annotatedElement().getParameterAnnotations();
1185         }
1186 }
1187
1188 class ConstructorAnnotationTester
1189                 extends AbstractMethodAnnotationTester<Constructor<?>> {
1190         public ConstructorAnnotationTester(ClassAnnotationTester clazz,
1191                         Constructor<?> constructor) {
1192                 super(clazz, constructor, constructor.getName(),
1193                                 constructor.getParameterTypes());
1194         }
1195
1196         protected Annotation[][] getParameterAnnotations() {
1197                 return annotatedElement().getParameterAnnotations();
1198         }
1199 }
1200
1201 class ClassAnnotationTester extends AnnotatedElementAnnotationTester<Class<?>> {
1202         private boolean isAnnotation;
1203         
1204         private Map<Constructor<?>, ConstructorAnnotationTester> constructors =
1205                 TestHelper.getMapFactory().
1206                 <Constructor<?>, ConstructorAnnotationTester>createMap();
1207         
1208         private Map<Method, MethodAnnotationTester> methods =
1209                 TestHelper.getMapFactory().
1210                 <Method, MethodAnnotationTester>createMap();
1211         
1212         private Map<String, FieldAnnotationTester> fields =
1213                 TestHelper.getMapFactory().
1214                 <String, FieldAnnotationTester>createMap();
1215
1216         public ClassAnnotationTester(Class<?> clazz, boolean isAnnotation) {
1217                 super(null, clazz, clazz.getName());
1218
1219                 this.isAnnotation = isAnnotation;
1220         }
1221
1222         public ClassAnnotationTester(Class<?> clazz) {
1223                 this(clazz, false);
1224         }
1225
1226         public boolean isAnnotation() {
1227                 return isAnnotation;
1228         }
1229
1230         public FieldAnnotationTester addField(String name)
1231                         throws SecurityException, NoSuchFieldException {
1232                 FieldAnnotationTester field = new FieldAnnotationTester(this,
1233                                 annotatedElement().getField(name));
1234
1235                 if (fields.put(name, field) != null)
1236                         throw new IllegalArgumentException("field already defined");
1237
1238                 return field;
1239         }
1240
1241         public MethodAnnotationTester addMethod(String name, Object defalutValue,
1242                         Class<?>... parameterTypes) throws SecurityException,
1243                         NoSuchMethodException {
1244                 Method reflMethod = annotatedElement().getMethod(
1245                                 name, parameterTypes);
1246                 MethodAnnotationTester method = new MethodAnnotationTester(this,
1247                                 reflMethod, defalutValue);
1248
1249                 if (methods.put(reflMethod, method) != null)
1250                         throw new IllegalArgumentException("method already defined");
1251
1252                 return method;
1253         }
1254
1255         public ConstructorAnnotationTester addConstructor(
1256                         Class<?>... parameterTypes) throws SecurityException,
1257                         NoSuchMethodException {
1258                 Constructor<?> reflConstructor =
1259                         annotatedElement().getConstructor(parameterTypes);
1260                 ConstructorAnnotationTester constructor =
1261                         new ConstructorAnnotationTester(this, reflConstructor);
1262
1263                 if (constructors.put(reflConstructor, constructor) != null)
1264                         throw new IllegalArgumentException("constructor already defined");
1265
1266                 return constructor;
1267         }
1268         
1269         protected void logName() {
1270                 TestHelper.log("== Testing %s ==", getName());
1271         }
1272
1273         public boolean test() throws SecurityException, NoSuchMethodException {
1274                 boolean ok = super.test();
1275                 
1276                 ok = testIsAnnotation() && ok;
1277
1278                 logHeader("Constructors");
1279                 for (ConstructorAnnotationTester tester :
1280                                 TestHelper.sorted(constructors.values())) {
1281                         ok = tester.test() && ok;
1282                 }
1283
1284                 logHeader("Methods");
1285                 for (MethodAnnotationTester tester :
1286                                 TestHelper.sorted(methods.values())) {
1287                         ok = tester.test() && ok;
1288                 }
1289
1290                 logHeader("Fields");
1291                 for (FieldAnnotationTester tester : 
1292                                 TestHelper.sorted(fields.values())) {
1293                         ok = tester.test() && ok;
1294                 }
1295
1296                 log();
1297                 return ok;
1298         }
1299         
1300         private boolean testIsAnnotation() {
1301                 logHeader("isAnnotation()");
1302                 
1303                 return TestHelper.okx(
1304                                 isAnnotation == annotatedElement().isAnnotation(),
1305                                 "test if the isAnnotation attribute is set properly",
1306                                 annotatedElement().getName(),
1307                                 (isAnnotation ? "class should be an annotation, but isn't!"
1308                                                 : "class should NOT be an annotation, but it is!"),
1309                                 EMPTY_OBJECT_ARRAY);
1310         }
1311 }
1312
1313 /* ********* the testcases ****************************************************/
1314
1315 /**
1316  * Test Annotations onto enums and their values.
1317  */
1318 @AnnotationB(string = "onto a enum")
1319 enum EnumA {
1320         @AnnotationB(string = "onto a enum field")
1321         VALUE1,
1322         VALUE2,
1323         VALUE3
1324 }
1325
1326 /**
1327  * Test Annotations on Annotations and their methods. Test Annotation on itself.
1328  */
1329 @Retention(value = RetentionPolicy.RUNTIME)
1330 @AnnotationA(
1331                 integer = 1,
1332                 string  = "onto itself",
1333                 classes = {AnnotationA.class, Class.class},
1334                 enumeration = EnumA.VALUE2)
1335 @interface AnnotationA {
1336         @AnnotationA(
1337                         integer = 2,
1338                         string  = "onto a method of itself")
1339         int        integer();
1340         String     string();
1341         Class<?>[] classes()     default {EnumA.class, Object[][].class};
1342         EnumA      enumeration() default EnumA.VALUE1;
1343 }
1344
1345 /**
1346  * This Annotation will be inherited as Annotation of a derived class.
1347  * Inheritance applies only for class annotations, not for methods or fields.
1348  */
1349 @Inherited
1350 @Retention(value = RetentionPolicy.RUNTIME)
1351 @interface AnnotationB {
1352         String string();
1353         Class<?> clazz() default AnnotationB.class;
1354 }
1355
1356 /**
1357  * Test all possible types of enum fields.
1358  */
1359 @Retention(value = RetentionPolicy.RUNTIME)
1360 @interface AnnotationC {
1361         byte               aByte()            default 100;
1362         char               aChar()            default 'Ăź';
1363         short              aShort()           default 88;
1364         int                aInt()             default Integer.MIN_VALUE;
1365         long               aLong()            default Long.MAX_VALUE;
1366         float              aFloat()           default 23.42f;
1367         double             aDouble()          default 555.0815d;
1368         String             aString()          default "Ă„Ă–Ăś";
1369         EnumA              aEnum()            default EnumA.VALUE2;
1370         Class<?>           aClass()           default EnumA.class;
1371         SuppressWarnings   aAnnotation()      default @SuppressWarnings("unchecked");
1372         byte[]             aByteArray()       default {(byte)255};
1373         char[]             aCharArray()       default {'ä', 'ö', 'ĂĽ'};
1374         short[]            aShortArray()      default {512};
1375         int[]              aIntArray()        default {640, 480};
1376         long[]             aLongArray()       default {1204l, 2048l};
1377         float[]            aFloatArray()      default {0.0f};
1378         double[]           aDoubleArray()     default {-2.2d, -3.3d};
1379         String[]           aStringArray()     default {""};
1380         EnumA[]            aEnumArray()       default EnumA.VALUE1;
1381         Class<?>[]         aClassArray()      default void.class;
1382         SuppressWarnings[] aAnnotationArray() default {};
1383 }
1384
1385 /**
1386  * This annotation will not be stored into the class file.
1387  */
1388 @interface AnnotationD {
1389 }
1390
1391 /**
1392  * Test annotations onto a class.
1393  */
1394 @AnnotationB(string = "onto a class", clazz = Foo.class)
1395 @AnnotationA(integer = 3, string = "onto a class")
1396 class Foo {
1397         /**
1398          * Test annotations onto a field.
1399          */
1400         @AnnotationA(integer = 4, string = "onto a field")
1401         public int afield;
1402
1403         /**
1404          * Test annotations onto a constructor.
1405          */
1406         @AnnotationA(integer = 9, string = "onto a constructor")
1407         public Foo() {
1408         }
1409
1410         /**
1411          * Test annotations onto a method.
1412          * 
1413          * @param x
1414          *            Test annotations onto a parameter.
1415          * @return
1416          */
1417         @AnnotationA(integer = 5, string = "onto a method")
1418         public int method(
1419                         @AnnotationA(
1420                                         integer = 6,
1421                                         string  = "onto a parameter")
1422                         int x) {
1423                 return x;
1424         }
1425
1426         /**
1427          * Test annotations onto a static method.
1428          * 
1429          * @param x
1430          *            Test annotations onto a parameter.
1431          * @return
1432          */
1433         @AnnotationA(integer = 7, string = "onto a static method")
1434         public static int staticMethod(
1435                         @AnnotationA(
1436                                         integer = 8,
1437                                         string  = "onto a parameter of a static method")
1438                         int x) {
1439                 return x;
1440         }
1441 }
1442
1443 /**
1444  * Test inheritance of annotations. Test all possible annotation field types as
1445  * default values. Test if an annotation without RetentionPolicy.RUNTIME is
1446  * really not visible at runtime.
1447  */
1448 @AnnotationC
1449 @AnnotationD
1450 class Bar extends Foo {
1451         /**
1452          * Test that superclass field annotations will not be visible here.
1453          */
1454         public int afield;
1455
1456         /**
1457          * Test that superclass constructor annotations will not be visible here.
1458          */
1459         public Bar() {
1460         }
1461
1462         /**
1463          * Test that superclass method (and parameter) annotations will not be
1464          * visible here.
1465          */
1466         public int method(int x) {
1467                 return x;
1468         }
1469
1470         /**
1471          * Test that superclass method (and parameter) annotations will not be
1472          * visible here.
1473          */
1474         public static int staticMethod(int x) {
1475                 return x;
1476         }
1477 }
1478
1479 /**
1480  * Test availability of annotations of inherited fields/methods. Test all
1481  * possible annotation field types. Test if not overloaded (=inherited)
1482  * methods/fields still have their annotations.
1483  */
1484 @AnnotationB(string = "onto a derived class", clazz = Baz.class)
1485 @AnnotationC(
1486                 aByte            = 0,
1487                 aChar            = 'a',
1488                 aShort           = 1,
1489                 aInt             = 2,
1490                 aLong            = 3l,
1491                 aFloat           = 4.4f,
1492                 aDouble          = 5.5d,
1493                 aString          = "a string",
1494                 aEnum            = EnumA.VALUE3,
1495                 aClass           = Class.class,
1496                 aAnnotation      = @SuppressWarnings("unchecked"),
1497                 aByteArray       = {0, 1, 2, 3},
1498                 aCharArray       = {'a', 'b', 'c'},
1499                 aShortArray      = 4,
1500                 aIntArray        = {5, 6, 7},
1501                 aLongArray       = {8l, 9l},
1502                 aFloatArray      = {10.10f, 11.11f, 12.12f},
1503                 aDoubleArray     = {},
1504                 aStringArray     = {"a string","another string"},
1505                 aEnumArray       = {EnumA.VALUE3, EnumA.VALUE3},
1506                 aClassArray      = {Class.class, Integer.class, Long.class},
1507                 aAnnotationArray = {
1508                         @SuppressWarnings(value = "unchecked"),
1509                         @SuppressWarnings(value = {"unused", "deprecation"})})
1510 class Baz extends Foo {
1511 }
1512
1513 /* ********* running the testcases ********************************************/
1514 public class TestAnnotations {
1515         @SuppressWarnings("unchecked")
1516         public static void main(String[] args) {
1517                 boolean ok = true;
1518                 MethodAnnotationTester mtester;
1519
1520                 try {
1521                         ClassAnnotationTester classEnumA       =
1522                                 new ClassAnnotationTester(EnumA.class);
1523                         ClassAnnotationTester classAnnotationA =
1524                                 new ClassAnnotationTester(AnnotationA.class, true);
1525                         ClassAnnotationTester classAnnotationB =
1526                                 new ClassAnnotationTester(AnnotationB.class, true);
1527                         ClassAnnotationTester classAnnotationC =
1528                                 new ClassAnnotationTester(AnnotationC.class, true);
1529                         ClassAnnotationTester classAnnotationD =
1530                                 new ClassAnnotationTester(AnnotationD.class, true);
1531                         ClassAnnotationTester classFoo         =
1532                                 new ClassAnnotationTester(Foo.class);
1533                         ClassAnnotationTester classBar         =
1534                                 new ClassAnnotationTester(Bar.class);
1535                         ClassAnnotationTester classBaz         =
1536                                 new ClassAnnotationTester(Baz.class);
1537
1538                         /* EnumA */
1539                         classEnumA.putDeclaredAnnotation(
1540                                         AnnotationB.class,
1541                                         new Entry("string", "onto a enum"),
1542                                         new Entry("clazz", AnnotationB.class));
1543                         classEnumA.addField("VALUE1").putDeclaredAnnotation(
1544                                         AnnotationB.class,
1545                                         new Entry("string", "onto a enum field"),
1546                                         new Entry("clazz", AnnotationB.class)
1547                         );
1548
1549                         /* AnnotationA */
1550                         classAnnotationA.putDeclaredAnnotation(
1551                                         Retention.class,
1552                                         new Entry("value", RetentionPolicy.RUNTIME)
1553                         );
1554                         classAnnotationA.putDeclaredAnnotation(
1555                                         AnnotationA.class,
1556                                         new Entry("integer", 1),
1557                                         new Entry("string",  "onto itself"),
1558                                         new Entry("classes", new Class<?>[] {
1559                                                         AnnotationA.class, Class.class}),
1560                                         new Entry("enumeration", EnumA.VALUE2)
1561                         );
1562                         classAnnotationA.addMethod("integer", null).putDeclaredAnnotation(
1563                                         AnnotationA.class,
1564                                         new Entry("integer", 2),
1565                                         new Entry("string", "onto a method of itself"),
1566                                         new Entry("classes", new Class<?>[] {
1567                                                         EnumA.class, Object[][].class }),
1568                                         new Entry("enumeration", EnumA.VALUE1)
1569                         );
1570                         classAnnotationA.addMethod("classes", 
1571                                         new Class<?>[] {EnumA.class, Object[][].class});
1572                         classAnnotationA.addMethod("enumeration", EnumA.VALUE1);
1573
1574                         /* AnnotationB */
1575                         classAnnotationB.putDeclaredAnnotation(Inherited.class);
1576                         classAnnotationB.putDeclaredAnnotation(
1577                                         Retention.class,
1578                                         new Entry("value", RetentionPolicy.RUNTIME)
1579                         );
1580                         classAnnotationB.addMethod("clazz", AnnotationB.class);
1581
1582                         /* AnnotationC */
1583                         classAnnotationC.putDeclaredAnnotation(
1584                                         Retention.class,
1585                                         new Entry("value", RetentionPolicy.RUNTIME)
1586                         );
1587
1588                         /* Foo */
1589                         classFoo.putDeclaredAnnotation(
1590                                         AnnotationB.class,
1591                                         new Entry("string", "onto a class"),
1592                                         new Entry("clazz",  Foo.class)
1593                         );
1594                         classFoo.putDeclaredAnnotation(
1595                                         AnnotationA.class,
1596                                         new Entry("integer", 3),
1597                                         new Entry("string",  "onto a class"),
1598                                         new Entry("classes", new Class<?>[] {
1599                                                         EnumA.class, Object[][].class}),
1600                                         new Entry("enumeration", EnumA.VALUE1)
1601                         );
1602                         classFoo.addField("afield").putDeclaredAnnotation(
1603                                         AnnotationA.class,
1604                                         new Entry("integer", 4),
1605                                         new Entry("string",  "onto a field"),
1606                                         new Entry("classes", new Class<?>[] {
1607                                                         EnumA.class, Object[][].class}),
1608                                         new Entry("enumeration", EnumA.VALUE1)
1609                         );
1610                         mtester = classFoo.addMethod("method", null, int.class);
1611                         mtester.putDeclaredAnnotation(
1612                                         AnnotationA.class,
1613                                         new Entry("integer", 5),
1614                                         new Entry("string",  "onto a method"),
1615                                         new Entry("classes", new Class<?>[] {
1616                                                         EnumA.class, Object[][].class}),
1617                                         new Entry("enumeration", EnumA.VALUE1)
1618                         );
1619                         mtester.putParameterAnnotation(0,
1620                                         AnnotationA.class,
1621                                         new Entry("integer", 6),
1622                                         new Entry("string", "onto a parameter"),
1623                                         new Entry("classes", new Class<?>[] {
1624                                                         EnumA.class, Object[][].class}),
1625                                         new Entry("enumeration", EnumA.VALUE1)
1626                         );
1627                         mtester = classFoo.addMethod("staticMethod", null, int.class);
1628                         mtester.putDeclaredAnnotation(
1629                                         AnnotationA.class,
1630                                         new Entry("integer", 7),
1631                                         new Entry("string", "onto a static method"),
1632                                         new Entry("classes", new Class<?>[] {
1633                                                         EnumA.class, Object[][].class }),
1634                                         new Entry("enumeration", EnumA.VALUE1)
1635                         );
1636                         mtester.putParameterAnnotation(0,
1637                                         AnnotationA.class,
1638                                         new Entry("integer", 8),
1639                                         new Entry("string", "onto a parameter of a static method"),
1640                                         new Entry("classes", new Class<?>[] {
1641                                                         EnumA.class, Object[][].class}),
1642                                         new Entry("enumeration", EnumA.VALUE1)
1643                         );
1644                         classFoo.addConstructor().putDeclaredAnnotation(
1645                                         AnnotationA.class,
1646                                         new Entry("integer", 9),
1647                                         new Entry("string", "onto a constructor"),
1648                                         new Entry("classes", new Class<?>[] {
1649                                                         EnumA.class, Object[][].class}),
1650                                         new Entry("enumeration", EnumA.VALUE1)
1651                         );
1652
1653                         /* Bar */
1654                         classBar.putInheritedAnnotation(
1655                                         AnnotationB.class,
1656                                         new Entry("string", "onto a class"),
1657                                         new Entry("clazz", Foo.class)
1658                         );
1659                         classBar.putDeclaredAnnotation(
1660                                         AnnotationC.class,
1661                                         new Entry("aByte",            (byte)100),
1662                                         new Entry("aChar",            (char)'Ăź'),
1663                                         new Entry("aShort",           (short)88),
1664                                         new Entry("aInt",             Integer.MIN_VALUE),
1665                                         new Entry("aLong",            Long.MAX_VALUE),
1666                                         new Entry("aFloat",           (float)23.42f),
1667                                         new Entry("aDouble",          (double)555.0815d),
1668                                         new Entry("aString",          "Ă„Ă–Ăś"),
1669                                         new Entry("aEnum",            EnumA.VALUE2),
1670                                         new Entry("aClass",           EnumA.class),
1671                                         new Entry("aAnnotation",      new AnnotationTester(
1672                                                         SuppressWarnings.class,
1673                                                         new Entry("value", new String[] {"unchecked"}))),
1674                                         new Entry("aByteArray",       new byte[]   {(byte) 255}),
1675                                         new Entry("aCharArray",       new char[]   {'ä', 'ö', 'ĂĽ'}),
1676                                         new Entry("aShortArray",      new short[]  {512}),
1677                                         new Entry("aIntArray",        new int[]    {640, 480}),
1678                                         new Entry("aLongArray",       new long[]   {1204l, 2048l}),
1679                                         new Entry("aFloatArray",      new float[]  {0.0f}),
1680                                         new Entry("aDoubleArray",     new double[] {-2.2d, -3.3d}),
1681                                         new Entry("aStringArray",     new String[] {""}),
1682                                         new Entry("aEnumArray",       new EnumA[]  {EnumA.VALUE1}),
1683                                         new Entry("aClassArray",      new Class<?>[] {void.class}),
1684                                         new Entry("aAnnotationArray", new AnnotationTester[] {})
1685                         );
1686                         classBar.addField("afield");
1687                         classBar.addMethod("method", null, int.class);
1688                         classBar.addMethod("staticMethod", null, int.class);
1689                         classBar.addConstructor();
1690
1691                         /* Baz */
1692                         classBaz.putDeclaredAnnotation(
1693                                         AnnotationB.class,
1694                                         new Entry("string", "onto a derived class"),
1695                                         new Entry("clazz",  Baz.class)
1696                         );
1697                         classBaz.putDeclaredAnnotation(
1698                                         AnnotationC.class,
1699                                         new Entry("aByte",            (byte)0),
1700                                         new Entry("aChar",            (char)'a'),
1701                                         new Entry("aShort",           (short)1),
1702                                         new Entry("aInt",             (int)2),
1703                                         new Entry("aLong",            (long)3l),
1704                                         new Entry("aFloat",           (float)4.4f),
1705                                         new Entry("aDouble",          (double)5.5d),
1706                                         new Entry("aString",          "a string"),
1707                                         new Entry("aEnum",            EnumA.VALUE3),
1708                                         new Entry("aClass",           Class.class),
1709                                         new Entry("aAnnotation",      new AnnotationTester(
1710                                                         SuppressWarnings.class,
1711                                                         new Entry("value",new String[] {"unchecked"}))),
1712                                         new Entry("aByteArray",       new byte[]  {0, 1, 2, 3}),
1713                                         new Entry("aCharArray",       new char[]  {'a', 'b', 'c'}),
1714                                         new Entry("aShortArray",      new short[] {4}),
1715                                         new Entry("aIntArray",        new int[]   {5, 6, 7}),
1716                                         new Entry("aLongArray",       new long[]  {8l, 9l}), 
1717                                         new Entry("aFloatArray",      new float[] {
1718                                                         10.10f, 11.11f, 12.12f}),
1719                                         new Entry("aDoubleArray",     new double[] {}),
1720                                         new Entry("aStringArray",     new String[] {
1721                                                         "a string",     "another string"}),
1722                                         new Entry("aEnumArray",       new EnumA[] {
1723                                                         EnumA.VALUE3, EnumA.VALUE3}),
1724                                         new Entry("aClassArray",      new Class<?>[] {
1725                                                         Class.class, Integer.class, Long.class}),
1726                                         new Entry("aAnnotationArray", new AnnotationTester[] {
1727                                                         new AnnotationTester(
1728                                                                         SuppressWarnings.class,
1729                                                                         new Entry("value", new String[] {
1730                                                                                         "unchecked"})),
1731                                                         new AnnotationTester(
1732                                                                         SuppressWarnings.class,
1733                                                                         new Entry("value", new String[] {
1734                                                                                         "unused", "deprecation"}))})
1735                         );
1736                         classBaz.addField("afield").putDeclaredAnnotation(
1737                                         AnnotationA.class,
1738                                         new Entry("integer", 4),
1739                                         new Entry("string", "onto a field"),
1740                                         new Entry("classes", new Class<?>[] {
1741                                                         EnumA.class, Object[][].class}),
1742                                         new Entry("enumeration", EnumA.VALUE1)
1743                         );
1744                         mtester = classBaz.addMethod("method", null, int.class);
1745                         mtester.putDeclaredAnnotation(
1746                                         AnnotationA.class,
1747                                         new Entry("integer", 5),
1748                                         new Entry("string",  "onto a method"),
1749                                         new Entry("classes", new Class<?>[] {
1750                                                         EnumA.class, Object[][].class }),
1751                                         new Entry("enumeration", EnumA.VALUE1)
1752                         );
1753                         mtester.putParameterAnnotation(0,
1754                                         AnnotationA.class,
1755                                         new Entry("integer", 6),
1756                                         new Entry("string",  "onto a parameter"),
1757                                         new Entry("classes", new Class<?>[] {
1758                                                         EnumA.class, Object[][].class }),
1759                                         new Entry("enumeration", EnumA.VALUE1)
1760                         );
1761                         mtester = classBaz.addMethod("staticMethod", null, int.class);
1762                         mtester.putDeclaredAnnotation(
1763                                         AnnotationA.class,
1764                                         new Entry("integer",     7),
1765                                         new Entry("string",      "onto a static method"),
1766                                         new Entry("classes",     new Class<?>[] {
1767                                                         EnumA.class, Object[][].class}),
1768                                         new Entry("enumeration", EnumA.VALUE1)
1769                         );
1770                         mtester.putParameterAnnotation(0,
1771                                         AnnotationA.class,
1772                                         new Entry("integer", 8),
1773                                         new Entry("string",      "onto a parameter of a static method"),
1774                                         new Entry("classes", new Class<?>[] {
1775                                                         EnumA.class, Object[][].class}),
1776                                         new Entry("enumeration", EnumA.VALUE1)
1777                         );
1778
1779                         ok = classEnumA.test();
1780                         ok = classAnnotationA.test() && ok;
1781                         ok = classAnnotationB.test() && ok;
1782                         ok = classAnnotationC.test() && ok;
1783                         ok = classAnnotationD.test() && ok;
1784                         ok = classFoo.test() && ok;
1785                         ok = classBar.test() && ok;
1786                         ok = classBaz.test() && ok;
1787                 } catch (Exception e) {
1788                         ok = TestHelper.ok(false, "exception free execution\n");
1789                         e.printStackTrace();
1790                 }
1791                 
1792                 TestHelper.printStatistics();
1793
1794                 if (!ok) {
1795                         System.exit(1);
1796                 }
1797         }
1798 }