View Javadoc

1   /*
2    * This file is part of hyphenType. hyphenType is free software: you can
3    * redistribute it and/or modify it under the terms of the GNU General Public
4    * License as published by the Free Software Foundation, either version 3 of the
5    * License, or (at your option) any later version. hyphenType is distributed in
6    * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
8    * the GNU General Public License for more details. You should have received a
9    * copy of the GNU General Public License along with hyphenType. If not, see
10   * <http://www.gnu.org/licenses/>.
11   */
12  package org.hyphenType.util;
13  
14  import java.lang.annotation.Annotation;
15  import java.lang.reflect.Array;
16  import java.lang.reflect.Field;
17  import java.lang.reflect.InvocationHandler;
18  import java.lang.reflect.InvocationTargetException;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Proxy;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Hashtable;
24  import java.util.ResourceBundle;
25  
26  import org.hyphenType.util.soc.StringObjectConversion;
27  import org.hyphenType.util.soc.StringParsingError;
28  
29  /**
30   * @author Aurelio Akira M. Matsui
31   */
32  public final class DefaultAnnotation implements InvocationHandler {
33  
34      // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
35      // STATIC SECTION
36      // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37  
38      public static final String METHOD_SEPARATOR = ".";
39      public static final String FIELD_SEPARATOR = ".";
40      public static final String ANNOTATION_SEPARATOR = "@";
41      
42      /**
43       * @param clazz TODO
44       * @return TODO
45       */
46      @SuppressWarnings("unchecked")
47      public static Annotation[] getAnnotations(final Class clazz) {
48  
49          Annotation[] annotations = clazz.getAnnotations();
50          Annotation[] result = new Annotation[annotations.length];
51          for (int i = 0; i < annotations.length; i++) {
52              result[i] = getAnnotation(clazz, annotations[i].annotationType());
53          }
54          return result;
55      }
56  
57      /**
58       * @param <T>
59       *            TODO
60       * @param clazz
61       *            TODO
62       * @param annotationClass
63       *            TODO
64       * @return TODO
65       */
66      @SuppressWarnings("unchecked")
67      public static <T extends Annotation> T getAnnotation(final Class clazz, final Class<T> annotationClass) {
68  //        if (clazz.isAnnotationPresent(annotationClass)) {
69              Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { annotationClass });
70              try {
71                  return (T) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation(annotationClass, clazz.getAnnotation(annotationClass), clazz) });
72              } catch (IllegalArgumentException e) {
73                  e.printStackTrace();
74              } catch (SecurityException e) {
75                  e.printStackTrace();
76              } catch (InstantiationException e) {
77                  e.printStackTrace();
78              } catch (IllegalAccessException e) {
79                  e.printStackTrace();
80              } catch (InvocationTargetException e) {
81                  e.printStackTrace();
82              } catch (NoSuchMethodException e) {
83                  e.printStackTrace();
84              }
85  //        } else {
86  //            return getAnnotationWithDefaults(annotationClass);
87  //        }
88          return null;
89      }
90  
91      /**
92       * @param clazz
93       *            TODO
94       * @return TODO
95       */
96      public static Annotation[] getAnnotations(final Method method) {
97  
98          Annotation[] annotations = method.getAnnotations();
99          Annotation[] result = new Annotation[annotations.length];
100         for (int i = 0; i < annotations.length; i++) {
101             result[i] = getAnnotation(method, annotations[i].annotationType());
102         }
103         return result;
104     }
105 
106     /**
107      * @param <T>
108      *            TODO
109      * @param clazz
110      *            TODO
111      * @param annotationClass
112      *            TODO
113      * @return TODO
114      */
115     @SuppressWarnings("unchecked")
116     public static <T extends Annotation> T getAnnotation(final Method method, final Class<T> annotationClass) {
117 //        if (method.isAnnotationPresent(annotationClass)) {
118             Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { annotationClass });
119             try {
120                 return (T) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation(annotationClass, method.getAnnotation(annotationClass), method) });
121             } catch (IllegalArgumentException e) {
122                 e.printStackTrace();
123             } catch (SecurityException e) {
124                 e.printStackTrace();
125             } catch (InstantiationException e) {
126                 e.printStackTrace();
127             } catch (IllegalAccessException e) {
128                 e.printStackTrace();
129             } catch (InvocationTargetException e) {
130                 e.printStackTrace();
131             } catch (NoSuchMethodException e) {
132                 e.printStackTrace();
133             }
134 //        } else {
135 //            return getAnnotationWithDefaults(annotationClass);
136 //        }
137         return null;
138     }
139     
140     /**
141      * @param clazz
142      *            TODO
143      * @return TODO
144      */
145     public static Annotation[] getAnnotations(final Field field) {
146 
147         Annotation[] annotations = field.getAnnotations();
148         Annotation[] result = new Annotation[annotations.length];
149         for (int i = 0; i < annotations.length; i++) {
150             result[i] = getAnnotation(field, annotations[i].annotationType());
151         }
152         return result;
153     }
154 
155     /**
156      * @param <T>
157      *            TODO
158      * @param clazz
159      *            TODO
160      * @param annotationClass
161      *            TODO
162      * @return TODO
163      */
164     @SuppressWarnings("unchecked")
165     public static <T extends Annotation> T getAnnotation(final Field field, final Class<T> annotationClass) {
166 //        if (field.isAnnotationPresent(annotationClass)) {
167             Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { annotationClass });
168             try {
169                 return (T) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation(annotationClass, field.getAnnotation(annotationClass), field) });
170             } catch (IllegalArgumentException e) {
171                 e.printStackTrace();
172             } catch (SecurityException e) {
173                 e.printStackTrace();
174             } catch (InstantiationException e) {
175                 e.printStackTrace();
176             } catch (IllegalAccessException e) {
177                 e.printStackTrace();
178             } catch (InvocationTargetException e) {
179                 e.printStackTrace();
180             } catch (NoSuchMethodException e) {
181                 e.printStackTrace();
182             }
183 //        } else {
184 //            return getAnnotationWithDefaults(annotationClass);
185 //        }
186         return null;
187     }
188     
189     /**
190      * @param enumConstant
191      *            An enumeration constant.
192      * @return TODO
193      */
194     public static Annotation[] getAnnotations(final Enum<?> enumConstant) {
195         Field f;
196         try {
197             f = enumConstant.getClass().getField(enumConstant.name());
198         } catch (SecurityException e) {
199             e.printStackTrace();
200             return null;
201         } catch (NoSuchFieldException e) {
202             e.printStackTrace();
203             return null;
204         }
205         
206         return getAnnotations(f);
207     }
208 
209     /**
210      * @param <T>
211      *            TODO
212      * @param enumConstant
213      *            An enumeration constant.
214      * @param annotationClass
215      *            TODO
216      * @return TODO
217      */
218     public static <T extends Annotation> T getAnnotation(final Enum<?> enumConstant, final Class<T> annotationClass) {
219         Field f;
220         try {
221             f = enumConstant.getClass().getField(enumConstant.name());
222         } catch (SecurityException e) {
223             e.printStackTrace();
224             return null;
225         } catch (NoSuchFieldException e) {
226             e.printStackTrace();
227             return null;
228         }
229         
230         return getAnnotation(f, annotationClass);
231     }
232     
233     /**
234      * @param <T>
235      *            TODO
236      * @param annotationClass
237      *            TODO
238      * @return TODO
239      */
240     @SuppressWarnings("unchecked")
241     public static <T extends Annotation> T getAnnotationWithDefaults(final Class<? extends Annotation> annotationClass) {
242         try {
243             Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { annotationClass });
244             return (T) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation(annotationClass) });
245         } catch (Exception e) {
246             e.printStackTrace();
247             return null;
248         }
249     }
250 
251     /**
252      * @param annotation
253      *            TODO
254      * @param method
255      *            TODO
256      * @param value
257      *            TODO
258      */
259     public static void set(final Annotation annotation, final Method method, final Object value) {
260 
261         if (!isDefaultAnnotation(annotation)) {
262             throw new IllegalArgumentException("The first argument should be an annotation retrieved from " + DefaultAnnotation.class.getName());
263         }
264         DefaultAnnotation da = (DefaultAnnotation) Proxy.getInvocationHandler(annotation);
265         da.values.put(method, value);
266     }
267 
268     /**
269      * @param annotation
270      *            TODO
271      * @param methodName
272      *            TODO
273      * @param value
274      *            TODO
275      */
276     public static void set(final Annotation annotation, final String methodName, final Object value) {
277 
278         Method method;
279 
280         try {
281             method = annotation.annotationType().getMethod(methodName);
282         } catch (SecurityException e) {
283             throw new IllegalArgumentException(e);
284         } catch (NoSuchMethodException e) {
285             throw new IllegalArgumentException(e);
286         }
287 
288         InvocationHandler ih = Proxy.getInvocationHandler(annotation);
289         if (!(ih instanceof DefaultAnnotation)) {
290             throw new IllegalArgumentException("The first argument should be an annotation retrieved from " + DefaultAnnotation.class.getName());
291         }
292         DefaultAnnotation da = (DefaultAnnotation) ih;
293         da.values.put(method, value);
294     }
295 
296     /**
297      * @param annotation
298      *            TODO
299      * @param resourceBundle
300      *            TODO
301      */
302     public static void fillWithResourceBundle(final Annotation annotation, final ResourceBundle resourceBundle) {
303         recursiveFillWithResourceBundle(annotation, resourceBundle, annotation.annotationType().getName());
304     }
305 
306     /**
307      * TODO A big, huge one! We still need to check if the contents of the resource bundle
308      * are providing everything the annotation needs. It is expected that annotations
309      * always have all its properties set. This requirement is so strong that it is
310      * reinforced by compilation checks. In this method, we are not checking this condition,
311      * so we urgently need to take a look at this. 
312      * 
313      * @param annotation
314      *            TODO
315      * @param resourceBundle
316      *            TODO
317      * @param key
318      *            TODO
319      */
320     @SuppressWarnings("unchecked")
321     private static void recursiveFillWithResourceBundle(final Annotation annotation, final ResourceBundle resourceBundle, final String key) {
322 
323         Class<? extends Annotation> anClass = annotation.annotationType();
324         DefaultAnnotation defaultAnnotation = (DefaultAnnotation) Proxy.getInvocationHandler(annotation);
325 
326         for (Method method : anClass.getDeclaredMethods()) {
327 
328             String newkey = key + METHOD_SEPARATOR + method.getName();
329             if (method.getReturnType().isAnnotation()) {
330                 Annotation subAnnotation = (Annotation) defaultAnnotation.values.get(method);
331                 recursiveFillWithResourceBundle(subAnnotation, resourceBundle, newkey);
332                 set(annotation, method, subAnnotation);
333             } else {
334                 try {
335 
336                     if (method.getReturnType().isArray()) {
337 
338                         if (method.getReturnType().getComponentType().isAnnotation()) {
339 
340                             /* The following pattern for arrays:
341                              * key = [ a, b, c]
342                              * is not allowed for annotations, since annotations need to have properties as in:
343                              * key[0].property1 = p1value
344                              * key[0].property2 = p2value
345                              * key[1].property1 = ...
346                              * key[1].property2 = ...
347                              */
348                             
349                             Object array = defaultAnnotation.invoke(null, method, new Object[] {});
350 
351                             int i = 0;
352                             String arrayKey = newkey + "[" + i + "]";
353                             boolean hasPrefix = hasPrefix(resourceBundle, arrayKey);
354                             
355                             ArrayList<Annotation> newArrayValue = new ArrayList<Annotation>();
356                             
357                             /* Here we need to split the algorithm into two parts. If the array
358                              * is null, that means we are creating a new array from scratch. So
359                              * we should simply ignore the "array" object. Conversely, if the
360                              * array is not null, we overwrite its values when the resource
361                              * bundle is offering an alternative and we keep the values from
362                              * the original array when there is no alternative.
363                              */
364                             if (array == null) {
365                                 while (hasPrefix) {
366                                     Annotation subAnnotation = DefaultAnnotation.getAnnotationWithDefaults((Class<? extends Annotation>) method.getReturnType().getComponentType());
367                                     recursiveFillWithResourceBundle(subAnnotation, resourceBundle, arrayKey);
368                                     newArrayValue.add(subAnnotation);
369                                     i++;
370                                     arrayKey = newkey + "[" + i + "]";
371                                     hasPrefix = hasPrefix(resourceBundle, arrayKey);
372                                 }
373                             } else {
374                                 while (hasPrefix || i < Array.getLength(array)) {
375                                     if (i >= Array.getLength(array)) {
376                                         // New value from resource bundle
377                                         Annotation subAnnotation = DefaultAnnotation.getAnnotationWithDefaults((Class<? extends Annotation>) method.getReturnType().getComponentType());
378                                         recursiveFillWithResourceBundle(subAnnotation, resourceBundle, arrayKey);
379                                         newArrayValue.add(subAnnotation);
380                                     } else if (hasPrefix) {
381                                         // Replacing old value with one from resource bundle
382                                         Annotation subAnnotation = (Annotation) Array.get(array, i);
383                                         recursiveFillWithResourceBundle(subAnnotation, resourceBundle, arrayKey);
384                                         newArrayValue.add(subAnnotation);
385                                     } else {
386                                         // Keeping old value
387                                         newArrayValue.add((Annotation) Array.get(array, i));
388                                     }
389                                     i++;
390                                     arrayKey = newkey + "[" + i + "]";
391                                     hasPrefix = hasPrefix(resourceBundle, arrayKey);
392                                 }
393                             }
394                             
395                             
396                             defaultAnnotation.values.put(method, newArrayValue.toArray((Object[]) Array.newInstance(method.getReturnType().getComponentType(), newArrayValue.size())));
397                         } else {
398                             
399                             /* If the array is described using a pattern such as:
400                              * key = [ a, b, c] 
401                              */
402                             if(resourceBundle.containsKey(defaultAnnotation.getBasePrefix() + newkey)) {
403                                 set(annotation, method, StringObjectConversion.fromString(method.getReturnType(), resourceBundle.getString(defaultAnnotation.getBasePrefix() + newkey)));
404                             }
405                             /* If the pattern is:
406                              * key[0] = a
407                              * key[1] = b
408                              * key[2] = c
409                              */
410                             else {
411                                 ArrayList<String> value = new ArrayList<String>();
412                                 int i = 0;
413                                 String arrayKey = newkey + "[" + i + "]";
414                                 while (resourceBundle.containsKey(arrayKey)) {
415                                     value.add(resourceBundle.getString(arrayKey));
416                                     i++;
417                                     arrayKey = newkey + "[" + i + "]";
418                                 }
419                                 
420                                 if (value.size() > 0) {
421                                     Object[] array = (Object[]) Array.newInstance(method.getReturnType().getComponentType(), value.size());
422                                     set(annotation, method, value.toArray(array));
423                                 }
424                             }
425                         }
426                     } else {
427                         if(resourceBundle.containsKey(defaultAnnotation.getBasePrefix() + newkey)) {
428                             set(annotation, method, StringObjectConversion.fromString(method.getReturnType(), resourceBundle.getString(defaultAnnotation.getBasePrefix() + newkey)));
429                         } else if (resourceBundle.containsKey(newkey)) {
430                             set(annotation, method, StringObjectConversion.fromString(method.getReturnType(), resourceBundle.getString(newkey)));
431                         }
432                     }
433                 } catch (StringParsingError e) {
434                     e.printStackTrace();
435                 } catch (IllegalArgumentException e) {
436                     e.printStackTrace();
437                 } catch (IllegalAccessException e) {
438                     e.printStackTrace();
439                 } catch (InvocationTargetException e) {
440                     e.printStackTrace();
441                 } catch (Throwable e) {
442                     e.printStackTrace();
443                 }
444             }
445         }
446     }
447 
448     /**
449      * @param resourceBundle
450      *            TODO
451      * @param prefix
452      *            TODO
453      * @return TODO
454      */
455     private static boolean hasPrefix(final ResourceBundle resourceBundle, final String prefix) {
456         for (String s : resourceBundle.keySet()) {
457             if (s.startsWith(prefix)) {
458                 return true;
459             }
460         }
461         return false;
462     }
463 
464     /**
465      * @param annotation
466      *            TODO
467      * @return TODO
468      */
469     public static boolean isDefaultAnnotation(final Annotation annotation) {
470         InvocationHandler ih = Proxy.getInvocationHandler(annotation);
471         return ih instanceof DefaultAnnotation;
472     }
473 
474     // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
475     // NON-STATIC SECTION
476     // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
477 
478     /**
479      * 
480      */
481     private final Annotation annotation;
482     /**
483      * 
484      */
485     private final Class<? extends Annotation> annotationClass;
486     /**
487      * 
488      */
489     private final Hashtable<Method, Object> values = new Hashtable<Method, Object>();
490     /**
491      * 
492      */
493     private final String basePrefix;
494 
495     /**
496      * @param clazz
497      *            TODO
498      */
499     private DefaultAnnotation(final Class<? extends Annotation> clazz) {
500         annotation = null;
501         annotationClass = clazz;
502         basePrefix = null;
503         init();
504     }
505 
506     /**
507      * @param clazz
508      *            TODO
509      */
510     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Class<?> base) {
511         annotation = null;
512         annotationClass = clazz;
513         basePrefix = base.getName() + ANNOTATION_SEPARATOR;
514         init();
515     }
516 
517     /**
518      * @param clazz
519      *            TODO
520      */
521     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Method base) {
522         annotation = null;
523         annotationClass = clazz;
524         basePrefix = base.getDeclaringClass().getName() + METHOD_SEPARATOR + base.getName() + ANNOTATION_SEPARATOR;
525         init();
526     }
527 
528     /**
529      * @param clazz
530      *            TODO
531      */
532     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Field base) {
533         annotation = null;
534         annotationClass = clazz;
535         basePrefix = base.getDeclaringClass().getName() + FIELD_SEPARATOR + base.getName() + ANNOTATION_SEPARATOR;
536         init();
537     }
538 
539     /**
540      * @param clazz
541      *            TODO
542      * @param annotationObject
543      *            TODO
544      */
545     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Annotation annotationObject) {
546         annotation = annotationObject;
547         annotationClass = clazz;
548         basePrefix = null;
549         init();
550     }
551     
552     /**
553      * @param clazz
554      *            TODO
555      * @param annotationObject
556      *            TODO
557      */
558     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Annotation annotationObject, final Class<?> base) {
559         annotation = annotationObject;
560         annotationClass = clazz;
561         basePrefix = base.getName() + ANNOTATION_SEPARATOR;
562         init();
563     }
564     
565     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Annotation annotationObject, final Method base) {
566         annotation = annotationObject;
567         annotationClass = clazz;
568         basePrefix = base.getDeclaringClass().getName() + METHOD_SEPARATOR + base.getName() + ANNOTATION_SEPARATOR;
569         init();
570     }
571     
572     private DefaultAnnotation(final Class<? extends Annotation> clazz, final Annotation annotationObject, final Field base) {
573         annotation = annotationObject;
574         annotationClass = clazz;
575         basePrefix = base.getDeclaringClass().getName() + FIELD_SEPARATOR + base.getName() + ANNOTATION_SEPARATOR;
576         init();
577     }
578     
579     @SuppressWarnings("unchecked")
580     private void init() {
581         /*
582          * If any of the annotation fields (methods) is also an annotation or an
583          * array of annotations, we need these fields to be DefaultAnnotation
584          * dynamic proxies too, otherwise we won't be able to change their
585          * values later.
586          */
587         if (annotation != null) {
588             try {
589                 for (Method method : annotationClass.getDeclaredMethods()) {
590                     if (method.getReturnType().isAnnotation()) {
591                         Annotation subAnnotation = (Annotation) method.invoke(annotation, new Object[] {});
592                         Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { method.getReturnType() });
593                         Object dynamicProxy = proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation((Class<? extends Annotation>) method.getReturnType(), subAnnotation, method) });
594                         values.put(method, dynamicProxy);
595                     } else if (method.getReturnType().isArray() && method.getReturnType().getComponentType().isAnnotation()) {
596                         Object array = method.invoke(annotation, new Object[] {});
597                         Object subArray = Array.newInstance(method.getReturnType().getComponentType(), Array.getLength(array));
598                         for (int i = 0; i < Array.getLength(array); i++) {
599                             Annotation subAnnotation = (Annotation) Array.get(array, i);
600                             Class proxyClass = Proxy.getProxyClass(DefaultAnnotation.class.getClassLoader(), new Class[] { method.getReturnType().getComponentType() });
601                             Object dynamicProxy = proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { new DefaultAnnotation((Class<? extends Annotation>) method.getReturnType().getComponentType(), subAnnotation, method) });
602                             Array.set(subArray, i, dynamicProxy);
603                         }
604                         values.put(method, subArray);
605                     }
606                 }
607             } catch (IllegalArgumentException e) {
608                 e.printStackTrace();
609             } catch (IllegalAccessException e) {
610                 e.printStackTrace();
611             } catch (InvocationTargetException e) {
612                 e.printStackTrace();
613             } catch (SecurityException e) {
614                 e.printStackTrace();
615             } catch (InstantiationException e) {
616                 e.printStackTrace();
617             } catch (NoSuchMethodException e) {
618                 e.printStackTrace();
619             }
620         }
621     }
622 
623     @Override
624     public boolean equals(final Object obj) {
625         if (obj == null) {
626             return false;
627         }
628         try {
629             InvocationHandler ih = Proxy.getInvocationHandler(obj);
630             if (ih instanceof DefaultAnnotation) {
631                 DefaultAnnotation other = (DefaultAnnotation) ih;
632                 return this.values.equals(other.values);
633             } else {
634                 return false;
635             }
636         } catch (IllegalArgumentException e) {
637             return false;
638         }
639     }
640 
641     @Override
642     public int hashCode() {
643         return toString().hashCode();
644     }
645 
646     @Override
647     public String toString() {
648         try {
649             String keyValueString = "";
650             for (Method m : annotationClass.getDeclaredMethods()) {
651                 if (m.getReturnType().isArray()) {
652                     keyValueString += String.format("%s=%s, ", m.getName(), Arrays.toString((Object[]) invoke(null, m, new Object[] {})));
653                 } else {
654                     keyValueString += String.format("%s=%s, ", m.getName(), invoke(null, m, new Object[] {}));
655                 }
656             }
657 
658             // Removing the trailing comma and space characters.
659             if (annotationClass.getDeclaredMethods().length > 1) {
660                 keyValueString = keyValueString.substring(0, keyValueString.length() - 2);
661             }
662 
663             return String.format("@%s(%s)", annotationClass.getName(), keyValueString);
664         } catch (SecurityException e) {
665             e.printStackTrace();
666         } catch (Throwable e) {
667             e.printStackTrace();
668         }
669         return "";
670     }
671 
672     /**
673      * @return TODO
674      * @see Annotation#annotationType()
675      */
676     public Class<? extends Annotation> annotationType() {
677         return annotationClass;
678     }
679     
680     public String getBasePrefix() {
681         return basePrefix;
682     }
683 
684     @Override
685     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
686 
687         if (method.equals(Annotation.class.getMethod("equals", Object.class))) {
688             return this.equals(args[0]);
689         }
690         if (method.equals(Annotation.class.getMethod("hashCode"))) {
691             return this.hashCode();
692         }
693         if (method.equals(Annotation.class.getMethod("toString"))) {
694             return this.toString();
695         }
696         if (method.equals(Annotation.class.getMethod("annotationType"))) {
697             return this.annotationType();
698         }
699         if (method.equals(Object.class.getMethod("notify"))) {
700             this.notify();
701             return null;
702         }
703         if (method.equals(Object.class.getMethod("notifyAll"))) {
704             this.notifyAll();
705             return null;
706         }
707         if (method.equals(Object.class.getMethod("wait"))) {
708             this.wait();
709             return null;
710         }
711         if (method.equals(Object.class.getMethod("wait", long.class))) {
712             this.wait((Long) args[0]);
713             return null;
714         }
715         if (method.equals(Object.class.getMethod("wait", long.class, int.class))) {
716             this.wait((Long) args[0], (Integer) args[1]);
717             return null;
718         }
719 
720         if (values.containsKey(method)) {
721             return values.get(method);
722         }
723 
724         if (annotation != null) {
725             return method.invoke(annotation, args);
726         }
727 
728         return method.getDefaultValue();
729     }
730 }