001    
002    package net.sourceforge.retroweaver.runtime.java.lang.annotation;
003    
004    import java.io.IOException;
005    import java.io.InputStream;
006    import java.lang.reflect.Array;
007    import java.lang.reflect.Method;
008    import java.util.ArrayList;
009    import java.util.HashMap;
010    import java.util.Hashtable;
011    import java.util.LinkedList;
012    import java.util.List;
013    import java.util.Map;
014    
015    import net.sourceforge.retroweaver.runtime.java.lang.Enum;
016    
017    import org.objectweb.asm.AnnotationVisitor;
018    import org.objectweb.asm.Attribute;
019    import org.objectweb.asm.ClassReader;
020    import org.objectweb.asm.ClassVisitor;
021    import org.objectweb.asm.FieldVisitor;
022    import org.objectweb.asm.Label;
023    import org.objectweb.asm.MethodAdapter;
024    import org.objectweb.asm.MethodVisitor;
025    import org.objectweb.asm.Type;
026    
027    /**
028     * The Annotation Information Block.
029     *
030     * This is the runtime data structure that holds all of the annotation data in
031     * a form that Retroweaver's runtime can use easily. At weave time, we create 
032     * a public static transient field named [ANNOTATIONS_FIELD] of this type. At
033     * runtime, we parse the class file, read the annotation data, and populate this
034     * data structure.
035     *
036     * ( Method parameter annotations appear in the same order as method parameters,
037     *  and each parameter gets its own list of Annotations )
038     *
039     * @author Toby Reyelts
040     *
041     */
042    public class AIB implements ClassVisitor {
043    
044      private Class class_;
045      private Map<String,Annotation> classAnnotations;
046      private Map<String,Map<String,Annotation>> methodAnnotations;
047      private Map<String,ArrayList<Map<String,Annotation>>> methodParameterAnnotations;
048      private Map<String,Map<String,Annotation>> fieldAnnotations;
049      private Map<String,Annotation> inheritedClassAnnotations;
050      private Map<String,Object> cachedMethodDefaults;
051    
052      private AIB(Class c) {
053        classAnnotations = new HashMap<String,Annotation>();
054        methodAnnotations = new HashMap<String,Map<String,Annotation>>();
055        methodParameterAnnotations = new HashMap<String,ArrayList<Map<String,Annotation>>>();
056        fieldAnnotations = new HashMap<String,Map<String,Annotation>>();
057        inheritedClassAnnotations = new HashMap<String,Annotation>();
058    
059        this.class_ = c;
060    
061        readClassStream(c.getName(), this);
062      }
063    
064            private void readClassStream(final String name, final ClassVisitor cv) {
065                    String resource = "/" + name.replace('.', '/') + ".class";
066                    InputStream classStream = class_.getResourceAsStream(resource);
067                    try {
068                            ClassReader r = new ClassReader(classStream);
069                            r.accept(cv, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG
070                                            + ClassReader.SKIP_FRAMES);
071    
072                            Class parent = class_.getSuperclass();
073                            if (parent != null) {
074                                    AIB parentAib = getAib(parent);
075                                    for(Map.Entry<String, Annotation> entry: parentAib.inheritedClassAnnotations.entrySet()) {
076                                            inheritedClassAnnotations.put(entry.getKey(), entry.getValue());
077                                    }
078                            }
079                            // add the local annotations
080                            for(Map.Entry<String, Annotation> entry: classAnnotations.entrySet()) {
081                                    inheritedClassAnnotations.put(entry.getKey(), entry.getValue());
082                            }
083                    } catch (IOException e) {
084                            // Shouldn't generally happen
085                            throw new AnnotationFormatError(
086                                            "[Retroweaver] Unable to read annotation data for: " + name, e);
087                    } finally {
088                            try {
089                                    if (classStream != null) {
090                                            classStream.close();
091                                    }
092                            } catch (IOException e) { // NOPMD by xlv
093                            }
094                    }
095            }
096    
097      private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[]{};
098    
099      private static final Annotation[][] EMPTY_ANNOTATION_ARRAY_ARRAY = new Annotation[][]{};
100    
101      public Annotation[] getClassAnnotations() {
102              return inheritedClassAnnotations.values().toArray( EMPTY_ANNOTATION_ARRAY );
103      }
104    
105      public Annotation[] getDeclaredClassAnnotations() {
106              return classAnnotations.values().toArray( EMPTY_ANNOTATION_ARRAY );
107      }
108    
109      public <T extends Annotation> T getClassAnnotation(final Class<T> annotationType) {
110              return (T) inheritedClassAnnotations.get(annotationType.getName());
111      }
112    
113      public Annotation[] getFieldAnnotations(final String fieldName) {
114              final Map<String,Annotation> annotations = fieldAnnotations.get( fieldName );
115          return annotations.values().toArray( EMPTY_ANNOTATION_ARRAY );
116      }
117    
118      public <T extends Annotation> T getFieldAnnotation(final String fieldName, final Class<T> annotationType) {
119              final Map<String,Annotation> annotations = fieldAnnotations.get( fieldName );
120              return (T) annotations.get(annotationType.getName());
121      }
122    
123      private String getMethodIdentifier(final String methodName, final Class[] parameterTypes, final Class returnType) {
124                final StringBuilder b = new StringBuilder(methodName);
125                b.append('(');
126                for (Class c: parameterTypes) {
127                    b.append(Type.getDescriptor(c));
128                }
129                b.append(')').append(Type.getDescriptor(returnType));
130                return b.toString();
131              
132      }
133      public Annotation[] getMethodAnnotations(final String methodName, final Class[] parameterTypes, final Class returnType) {
134              final Map<String,Annotation> annotations = methodAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType));
135              if (annotations == null) {
136                      return EMPTY_ANNOTATION_ARRAY;
137              }
138          return annotations.values().toArray( EMPTY_ANNOTATION_ARRAY );      
139      }
140    
141      public <T extends Annotation> T getMethodAnnotation(final String methodName, final Class[] parameterTypes, final Class returnType, final Class<T> annotationType) {
142              final Map<String,Annotation> annotations = methodAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType));
143              if (annotations == null) {
144                      return null;
145              }
146              return (T) annotations.get(annotationType.getName());
147      }
148    
149      private Map<String, Object> getMethodDefaults() {
150              assert(class_.isAnnotation());
151    
152              if (cachedMethodDefaults == null) {
153                    DefaultValueVisitor v = new DefaultValueVisitor();
154                    cachedMethodDefaults = v.parseAttributes(class_.getName());
155              }
156    
157              return cachedMethodDefaults;
158      }
159    
160      private static final Method cloneMethod;
161    
162      static {
163            try {
164                    cloneMethod = Object.class.getDeclaredMethod("clone", new Class[0]);
165                    cloneMethod.setAccessible(true);
166            } catch (NoSuchMethodException e) {
167                    throw new RuntimeException(e.getMessage());
168            }
169      }
170    
171      public Object getDefaultValue(final String methodName) {
172        assert (class_.isAnnotation());
173    
174            Object o = getMethodDefaults().get(methodName);
175            if (o == null) {
176                    return null;
177            } else if (o.getClass().isArray()) {
178                    try {
179                            o = cloneMethod.invoke(o);
180                    } catch (Exception e) {
181                            throw new RuntimeException(e.getMessage());
182                    }
183            }
184            return o;
185      }
186    
187      public Annotation[][] getMethodParameterAnnotations(final String methodName, final Class[] parameterTypes, final Class returnType) {
188              ArrayList<Map<String, Annotation>> annotations = methodParameterAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType));
189    
190              if (annotations == null) {
191                      return EMPTY_ANNOTATION_ARRAY_ARRAY; // NOPMD by xlv
192              }
193    
194              if (annotations.size() != parameterTypes.length) {
195                      throw new AnnotationFormatError("inconsistent parameter count");
196              }
197    
198              Annotation[][] a = new Annotation[parameterTypes.length][];
199    
200              for(int i = 0; i < parameterTypes.length; i++) {
201                      Map<String, Annotation> map = annotations.get(i);
202                      
203                      a[i] = map.values().toArray(EMPTY_ANNOTATION_ARRAY);
204              }
205              
206              return a;
207      }
208    
209            private static final Map<Class, AIB> classDescriptors = new Hashtable<Class, AIB>();
210    
211            /**
212             * Returns the AIB for the class.
213             */
214            public static AIB getAib(final Class c) {
215                    synchronized (c) {
216                            AIB aib = classDescriptors.get(c);
217                            if (aib == null) {
218                                    aib = new AIB(c);
219                                    classDescriptors.put(c, aib);
220                            }
221                            return aib;
222                    }
223            }
224    
225            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
226                    if (!visible) { return EMPTY_VISITOR; }
227    
228                    return new TopLevelAnnotation(desc, classAnnotations);
229            }
230    
231            public FieldVisitor visitField(final int access, final String fieldName,
232                            final String desc, final String signature, final Object value) {
233                    return new FieldVisitor() {
234    
235                            Map<String,Annotation> annotations = new HashMap<String,Annotation>();
236                            
237                            public AnnotationVisitor visitAnnotation(String desc,
238                                            boolean visible) {
239                                    if (!visible) { return EMPTY_VISITOR; }
240    
241                                    return new TopLevelAnnotation(desc, annotations);
242                            }
243    
244                            public void visitAttribute(Attribute attr) {
245                                    // EMPTY
246                            }
247    
248                            public void visitEnd() {
249                                    fieldAnnotations.put(fieldName, annotations);
250                            }
251                    };
252            }
253    
254            public MethodVisitor visitMethod(final int access, final String methodName,
255                            final String desc, final String signature, final String[] exceptions) {
256                    return new MethodAdapter(EMPTY_VISITOR) {
257    
258                            Map<String,Annotation> ma = new HashMap<String,Annotation>();
259                            ArrayList<Map<String, Annotation>> pa = new ArrayList<Map<String, Annotation>>();
260    
261                            public AnnotationVisitor visitAnnotationDefault() {
262                                    return EMPTY_VISITOR;
263                            }
264    
265                            public AnnotationVisitor visitAnnotation(String desc,
266                                            boolean visible) {
267                                    if (!visible) { return EMPTY_VISITOR; }
268    
269                                    return new TopLevelAnnotation(desc, ma);
270                            }
271    
272                            public AnnotationVisitor visitParameterAnnotation(int parameter,
273                                            String desc, boolean visible) {
274                                    if (!visible) { return EMPTY_VISITOR; }
275    
276                                    Map<String, Annotation> map;
277                                    if (parameter < pa.size()) {
278                                            map = pa.get(parameter);
279                                    } else {
280                                            map = new HashMap<String, Annotation>();
281                                            pa.add(parameter, map);
282                                    }
283    
284                                    return new TopLevelAnnotation(desc, map);
285                            }
286    
287                            public void visitEnd() {
288                                    String name = methodName + desc;
289                                    methodAnnotations.put(name, ma);
290                                    methodParameterAnnotations.put(name, pa);
291                            }
292                    };
293            }
294    
295            public void visit(int version, int access, String name, String signature,
296                            String superName, String[] interfaces) {
297                    // EMPTY
298            }
299    
300            public void visitSource(String source, String debug) {
301                    // EMPTY
302            }
303    
304            public void visitOuterClass(String owner, String name, String desc) {
305                    // EMPTY
306            }
307    
308            public void visitAttribute(Attribute attr) {
309                    // EMPTY
310            }
311    
312            public void visitInnerClass(String name, String outerName,
313                            String innerName, int access) {
314                    // EMPTY
315            }
316    
317            public void visitEnd() {
318                    // EMPTY
319            }
320    
321            class DefaultValueVisitor implements ClassVisitor {
322     
323                    private final Map<String,Object> attributes = new HashMap<String,Object>();
324    
325                    Map<String,Object> parseAttributes(String className) {
326                            readClassStream(className, this);
327    
328                            return attributes;
329                    }
330    
331                    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
332                            return EMPTY_VISITOR;
333                    }
334    
335                    public FieldVisitor visitField(final int access, final String name,
336                                    final String desc, final String signature, final Object value) {
337                            return EMPTY_VISITOR;
338                    }
339    
340                    public MethodVisitor visitMethod(final int access, final String methodName,
341                                    final String desc, final String signature, final String[] exceptions) {
342                            return new MethodAdapter(EMPTY_VISITOR) {
343    
344                                    public AnnotationVisitor visitAnnotationDefault() {
345                                            // remove leading ()
346                                            String type = desc.substring(2);
347    
348                                            return new DefaultAnnotation(methodName, type, attributes);
349                                    }
350    
351                                    public AnnotationVisitor visitAnnotation(String desc,
352                                                    boolean visible) {
353                                            return EMPTY_VISITOR;
354                                    }
355    
356                                    public AnnotationVisitor visitParameterAnnotation(int parameter,
357                                                    String desc, boolean visible) {
358                                            return EMPTY_VISITOR;
359                                    }
360                            };
361                    }
362    
363                    public void visit(int version, int access, String name, String signature,
364                                    String superName, String[] interfaces) {
365                            // EMPTY
366                    }
367    
368                    public void visitSource(String source, String debug) {
369                            // EMPTY
370                    }
371    
372                    public void visitOuterClass(String owner, String name, String desc) {
373                            // EMPTY
374                    }
375    
376                    public void visitAttribute(Attribute attr) {
377                            // EMPTY
378                    }
379    
380                    public void visitInnerClass(String name, String outerName,
381                                    String innerName, int access) {
382                            // EMPTY
383                    }
384    
385                    public void visitEnd() {
386                            // EMPTY
387                    }
388            }
389    
390            abstract class AbstractAnnotationVisitor implements AnnotationVisitor {
391    
392                    protected final AbstractAnnotationVisitor parent;
393    
394                    protected final String className;
395    
396                    AbstractAnnotationVisitor(AbstractAnnotationVisitor parent, String className) {
397                            this.parent = parent;
398                            this.className = className;
399                    }
400    
401                    protected Class getClass(String name) {
402                            try {
403                                    return Class.forName(name, true, class_.getClassLoader());
404                            } catch (ClassNotFoundException e) {
405                                    throw new AnnotationFormatError(
406                                                    "[Retroweaver] Unable to find class: " + name, e);
407                            }
408                    }
409    
410                    protected Annotation createAnnotation(String className, Map<String, Object>attributes) {
411                            String internalName = Type.getType(className).getClassName();
412                            Class<? extends Annotation> type = (Class<? extends Annotation>) getClass(internalName);
413                            Annotation a = AnnotationImpl.createAnnotation(type, attributes);
414    
415                            return a;
416                    }
417    
418                    private Object getEnumValue(final String desc, final String value) {
419                            String name = Type.getType(desc).getClassName();
420                            Class c = getClass(name);
421                            return Enum.valueOf(c, value);
422                    }
423    
424                    abstract void insertValue(String name, Object value);
425    
426                    public void visit(String name, Object value) {
427                            Object v;
428                            if (value instanceof Type) {
429                                    Type t = (Type) value;
430                                    v = getClass(t.getClassName());
431                            } else {
432                                    v = value;
433                            }
434    
435                            insertValue(name, v);
436                    }
437    
438                    public void visitEnum(String name, String desc, String value) {
439                            insertValue(name, getEnumValue(desc, value));
440                    }
441    
442                    public AnnotationVisitor visitAnnotation(String name, String desc) {
443                            return new NestedAnnotation(this, desc);
444                    }
445    
446            }
447    
448            private class ArrayAnnotation extends AbstractAnnotationVisitor {
449    
450                    ArrayAnnotation(AbstractAnnotationVisitor parent, String className, String type) {
451                            super(parent, className);
452                            this.type = type;
453                    }
454    
455                    private final String type;
456    
457                    private final List<Object> values = new LinkedList<Object>();
458    
459                    void insertValue(String name, Object value) {
460                            values.add(value);
461                    }
462    
463                    public AnnotationVisitor visitArray(String name) {
464                            throw new UnsupportedOperationException("Nested arrays are not allowed");
465                    }
466    
467                    public void visitEnd() {
468                            Class c = getClass(type.replace('/', '.'));
469                            c = c.getComponentType();
470    
471                            Object a = Array.newInstance(c, values.size());
472                            if (!values.isEmpty()) {
473                                    a = values.toArray((Object[]) a);
474                            }
475    
476                            parent.insertValue(className, a);
477                    }
478            }
479    
480            private class NestedAnnotation extends AbstractAnnotationVisitor {              
481    
482                    NestedAnnotation(AbstractAnnotationVisitor parent, String className) {
483                            super(parent, className);
484                    }
485    
486                    private final Map<String, Object> attributes = new HashMap<String, Object>();
487    
488                    void insertValue(String name, Object value) {
489                            attributes.put(name, value);
490                    }
491    
492                    public AnnotationVisitor visitArray(String name) {
493                            throw new UnsupportedOperationException();
494                    }
495    
496                    public void visitEnd() {
497                            Annotation annotation = createAnnotation(className, attributes);
498    
499                            parent.insertValue(className, annotation);
500                    }
501    
502            }
503    
504            private class DefaultAnnotation extends AbstractAnnotationVisitor {
505                    
506                    DefaultAnnotation(String className, String type, Map<String, Object> attributes) {
507                            super(null, className);
508                            this.type = type;
509                            this.attributes = attributes;
510                    }
511    
512                    private final String type;
513    
514                    private final Map<String, Object> attributes;
515                    
516                    void insertValue(String name, Object value) {
517                            attributes.put(className, value);
518                    }
519    
520                    public AnnotationVisitor visitArray(String name) {
521                            return new ArrayAnnotation(this, className, type);
522                    }
523    
524                    public AnnotationVisitor visitAnnotation(String name, String desc) {
525                            return new NestedAnnotation(this, className);
526                    }
527    
528                    public void visitEnd() {
529                    }
530    
531            }
532    
533            private class TopLevelAnnotation extends AbstractAnnotationVisitor {
534    
535                    TopLevelAnnotation(String className, Map<String,Annotation> annotations) {
536                            super(null, className);
537    
538                            this.annotations = annotations;
539    
540                            // first get default values
541                            // then visitor methods are used to fill the custom settings
542                            String type = Type.getType(className).getClassName();
543                            attributes = new HashMap<String, Object>(getAib(getClass(type)).getMethodDefaults());
544                    }
545    
546                    private final Map<String,Annotation> annotations;
547    
548                    private final Map<String, Object> attributes;
549    
550                    private String getClassNameFromInternalName(final String name) {
551                            if (name.charAt(0) != 'L') {
552                                    return name;
553                            }
554    
555                            return name.replace('/', '.').substring(1, name.length()-1);
556                    }
557    
558                    void insertValue(String name, Object value) {
559                            String key = getClassNameFromInternalName(name);
560                            attributes.put(key, value);
561                    }
562    
563                    public AnnotationVisitor visitArray(String name) {
564                            try {
565                                    String type = Type.getType(className).getClassName();
566                                    Method m = Class.forName(type).getMethod(name, new Class[0]);
567                                    type = m.getReturnType().getName();
568                                    return new ArrayAnnotation(this, name, type);
569                            } catch (Exception e) {
570                                    throw new AnnotationFormatError(e);
571                            }
572                    }
573    
574                    public void visitEnd() {
575                            Annotation annotation = createAnnotation(className, attributes);
576    
577                            String key = getClassNameFromInternalName(className);
578                            annotations.put(key, annotation);
579                    }
580    
581            }
582            
583            public static final AIBEmptyVisitor EMPTY_VISITOR = new AIBEmptyVisitor();
584    
585            public static final class AIBEmptyVisitor implements ClassVisitor, FieldVisitor,
586                            MethodVisitor, AnnotationVisitor {
587    
588                    public void visit(final int version, final int access,
589                                    final String name, final String signature,
590                                    final String superName, final String[] interfaces) {
591                    }
592    
593                    public void visitSource(final String source, final String debug) {
594                    }
595    
596                    public void visitOuterClass(final String owner, final String name,
597                                    final String desc) {
598                    }
599    
600                    public AnnotationVisitor visitAnnotation(final String desc,
601                                    final boolean visible) {
602                            return this;
603                    }
604    
605                    public void visitAttribute(final Attribute attr) {
606                    }
607    
608                    public void visitInnerClass(final String name, final String outerName,
609                                    final String innerName, final int access) {
610                    }
611    
612                    public FieldVisitor visitField(final int access, final String name,
613                                    final String desc, final String signature, final Object value) {
614                            return this;
615                    }
616    
617                    public MethodVisitor visitMethod(final int access, final String name,
618                                    final String desc, final String signature,
619                                    final String[] exceptions) {
620                            return this;
621                    }
622    
623                    public void visitEnd() {
624                    }
625    
626                    public AnnotationVisitor visitAnnotationDefault() {
627                            return this;
628                    }
629    
630                    public AnnotationVisitor visitParameterAnnotation(final int parameter,
631                                    final String desc, final boolean visible) {
632                            return this;
633                    }
634    
635                    public void visitCode() {
636                    }
637    
638                    public void visitFrame(final int type, final int nLocal,
639                                    final Object[] local, final int nStack, final Object[] stack) {
640                    }
641    
642                    public void visitInsn(final int opcode) {
643                    }
644    
645                    public void visitIntInsn(final int opcode, final int operand) {
646                    }
647    
648                    public void visitVarInsn(final int opcode, final int var) {
649                    }
650    
651                    public void visitTypeInsn(final int opcode, final String desc) {
652                    }
653    
654                    public void visitFieldInsn(final int opcode, final String owner,
655                                    final String name, final String desc) {
656                    }
657    
658                    public void visitMethodInsn(final int opcode, final String owner,
659                                    final String name, final String desc) {
660                    }
661    
662                    public void visitJumpInsn(final int opcode, final Label label) {
663                    }
664    
665                    public void visitLabel(final Label label) {
666                    }
667    
668                    public void visitLdcInsn(final Object cst) {
669                    }
670    
671                    public void visitIincInsn(final int var, final int increment) {
672                    }
673    
674                    public void visitTableSwitchInsn(final int min, final int max,
675                                    final Label dflt, final Label labels[]) {
676                    }
677    
678                    public void visitLookupSwitchInsn(final Label dflt, final int keys[],
679                                    final Label labels[]) {
680                    }
681    
682                    public void visitMultiANewArrayInsn(final String desc, final int dims) {
683                    }
684    
685                    public void visitTryCatchBlock(final Label start, final Label end,
686                                    final Label handler, final String type) {
687                    }
688    
689                    public void visitLocalVariable(final String name, final String desc,
690                                    final String signature, final Label start, final Label end,
691                                    final int index) {
692                    }
693    
694                    public void visitLineNumber(final int line, final Label start) {
695                    }
696    
697                    public void visitMaxs(final int maxStack, final int maxLocals) {
698                    }
699    
700                    public void visit(final String name, final Object value) {
701                    }
702    
703                    public void visitEnum(final String name, final String desc,
704                                    final String value) {
705                    }
706    
707                    public AnnotationVisitor visitAnnotation(final String name,
708                                    final String desc) {
709                            return this;
710                    }
711    
712                    public AnnotationVisitor visitArray(final String name) {
713                            return this;
714                    }
715            }
716    
717    }
718