Package org.codehaus.backport175.reader.bytecode

Source Code of org.codehaus.backport175.reader.bytecode.AnnotationReader$MemberKey

/*******************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved.                      *
* http://backport175.codehaus.org                                                         *
* --------------------------------------------------------------------------------------- *
* The software in this package is published under the terms of Apache License Version 2.0 *
* a copy of which has been included with this distribution in the license.txt file.       *
*******************************************************************************************/
package org.codehaus.backport175.reader.bytecode;

import org.objectweb.asm.*;
import org.codehaus.backport175.reader.bytecode.spi.BytecodeProvider;
import org.codehaus.backport175.reader.proxy.ProxyFactory;
import org.codehaus.backport175.reader.Annotation;
import org.codehaus.backport175.reader.ReaderException;

import java.util.*;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;

/**
* Reads Java 5 {@link java.lang.annotation.RetentionPolicy.RUNTIME} annotations from the class' bytecode.
* <p/>
* Can be used with a custom implementation of the {@link org.codehaus.backport175.reader.bytecode.spi.BytecodeProvider}
* interface.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
*/
public class AnnotationReader {

    private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
    private static final String INIT_METHOD_NAME = "<init>";

    public static BytecodeProvider BYTECODE_PROVIDER = new DefaultBytecodeProvider();

    private static final Map READERS = new WeakHashMap();

    private final WeakReference m_classRef;

    // ===========================================================================
    // Implementation notes:
    // Parsing and annotation creation is made in two steps
    //
    // 1. The bytecode is parsed and the annotation content is put in elements,
    //    which are stored for later processing
    //
    // 2. Upon annotation access the elements are processed and a dynamic proxy
    //    for the annotation is created and cached.
    //
    // This gives much better performance than reflective access of Java 5
    // annotations (reflective access is around 5 times slower)
    // ===========================================================================

    private final Map m_classAnnotationElements = new HashMap();
    private final Map m_constructorAnnotationElements = new HashMap();
    private final Map m_methodAnnotationElements = new HashMap();
    private final Map m_fieldAnnotationElements = new HashMap();

    private final Map m_classAnnotationCache = new HashMap();
    private final Map m_constructorAnnotationCache = new HashMap();
    private final Map m_methodAnnotationCache = new HashMap();
    private final Map m_fieldAnnotationCache = new HashMap();

    /**
     * Sets the bytecode provider.
     * <p/>
     * If a custom provider is not set then a default impl will be used (which reads the bytecode from disk).
     *
     * @param bytecodeProvider
     */
    public static void setBytecodeProvider(final BytecodeProvider bytecodeProvider) {
        synchronized (BYTECODE_PROVIDER) {
            BYTECODE_PROVIDER = bytecodeProvider;
        }
    }

    /**
     * Returns the annotation reader for the class specified.
     * <p/>
     * The annotation reader is created and cached if non-existant.
     *
     * @param klass
     * @return the annotation reader
     */
    public static AnnotationReader getReaderFor(final Class klass) {
        final AnnotationReader reader;
        Object value = READERS.get(klass);
        if (value == null) {
            synchronized (READERS) {
                reader = new AnnotationReader(klass);
                READERS.put(klass, reader);
            }
        } else {
            reader = (AnnotationReader)value;
        }
        return reader;
    }

    /**
     * Resets the annotation reader for the class specified and triggers a new parsing of the newly read bytecode.
     * <p/>
     * This method calls <code>parse</code> and is therefore all the is needed to invoke to get a fully updated reader.
     *
     * @param klass
     */
    public static void refresh(final Class klass) {
        AnnotationReader reader = getReaderFor(klass);
        synchronized (reader) {
            reader.refresh();
        }
    }

    /**
     * Resets *all* the annotation reader and triggers a new parsing of the newly read bytecode.
     * <p/>
     * This method will force parsing of all classes bytecode which might be very time consuming, use with care.
     * <p/>
     * This method calls <code>parse</code> and is therefore all the is needed to invoke to get a fully updated reader.
     */
    public static void refreshAll() {
        for (Iterator it = READERS.values().iterator(); it.hasNext();) {
            AnnotationReader reader = (AnnotationReader)it.next();
            synchronized (reader) {
                reader.refresh();
            }
        }
    }

    /**
     * Converts the annotion class description to a Java class name.
     *
     * @param desc
     * @return
     */
    public static String toJavaName(final String desc) {
        return desc.substring(1, desc.length() - 1).replace('/', '.');
    }

    /**
     * Checks if an annotation is present at a specific class.
     *
     * @param annotationType the annotation type
     * @return true if the annotation is present else false
     */
    public boolean isAnnotationPresent(final Class annotationType) {
        return m_classAnnotationElements.containsKey(annotationType.getName());
    }

    /**
     * Returns the class annotation with the name specified.
     *
     * @param annotationName
     * @return the class annotation
     */
    public Annotation getAnnotation(final String annotationName) {
        Object cachedAnnotation = m_classAnnotationCache.get(annotationName);
        if (cachedAnnotation != null) {
            return (Annotation)cachedAnnotation;
        } else {
            final Annotation annotation;
            final AnnotationElement.Annotation annotationInfo =
                    (AnnotationElement.Annotation)m_classAnnotationElements.get(annotationName);
            if (annotationInfo != null) {
                annotation = ProxyFactory.newAnnotationProxy(
                        annotationInfo,
                        ((Class)m_classRef.get()).getClassLoader()
                );
                m_classAnnotationCache.put(annotationName, annotation);
                return annotation;
            } else {
                return null;
            }
        }
    }

    /**
     * Returns all the class annotations.
     *
     * @return an array with the class annotations
     */
    public Annotation[] getAnnotations() {
        final Collection annotationElements = m_classAnnotationElements.values();
        if (annotationElements.isEmpty()) {
            return EMPTY_ANNOTATION_ARRAY;
        }
        return getAnnotations(annotationElements, m_classAnnotationCache);
    }

    /**
     * Checks if an annotation is present at a specific constructor.
     *
     * @param annotationType the annotation type
     * @return true if the annotation is present else false
     */
    public boolean isAnnotationPresent(final Class annotationType, final Constructor constructor) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(constructor);
        Object map = m_constructorAnnotationElements.get(key);
        if (map != null) {
            if (((Map)map).containsKey(annotationType.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the constructor annotation with the name specified for the constructor specified.
     *
     * @param annotationName
     * @return the constructor annotation
     */
    public Annotation getAnnotation(final String annotationName, final Constructor constructor) {
        Map annotationMap = getAnnotationCacheFor(constructor);
        Object cachedAnnotation = annotationMap.get(annotationName);
        if (cachedAnnotation != null) {
            return (Annotation)cachedAnnotation;
        }
        // not in cache - create a new DP and put in cache
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(constructor);
        final Map annotations = (Map)m_constructorAnnotationElements.get(key);
        if (annotations == null) {
            // no such annotation
            return null;
        }
        Object annotationElement = annotations.get(annotationName);
        if (annotationElement != null) {
            Annotation annotation = ProxyFactory.newAnnotationProxy(
                    (AnnotationElement.Annotation)annotationElement,
                    constructor.getDeclaringClass().getClassLoader()
            );
            annotationMap.put(annotationName, annotation);
            return annotation;
        }
        return null;
    }

    /**
     * Returns all the constructor annotations.
     *
     * @param constructor the java.lang.reflect.Constructor object to find the annotations on.
     * @return an array with the constructor annotations
     */
    public Annotation[] getAnnotations(final Constructor constructor) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(constructor);
        Object map = m_constructorAnnotationElements.get(key);
        if (map != null) {
            final Collection annotationElements = ((Map)m_constructorAnnotationElements.get(key)).values();
            if (annotationElements.isEmpty()) {
                return EMPTY_ANNOTATION_ARRAY;
            }
            final Map cache = getAnnotationCacheFor(constructor);
            return getAnnotations(annotationElements, cache);
        } else {
            return EMPTY_ANNOTATION_ARRAY;
        }
    }

    /**
     * Checks if an annotation is present at a specific method.
     *
     * @param annotationType the annotation type
     * @return true if the annotation is present else false
     */
    public boolean isAnnotationPresent(final Class annotationType, final Method method) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(method);
        Object map = m_methodAnnotationElements.get(key);
        if (map != null) {
            if (((Map)m_methodAnnotationElements.get(key)).containsKey(annotationType.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the method annotation with the name specified for the method specified.
     *
     * @param annotationName
     * @return the method annotation
     */
    public Annotation getAnnotation(final String annotationName, final Method method) {
        Map annotationMap = (Map)m_methodAnnotationCache.get(method);
        if (annotationMap == null) {
            annotationMap = new HashMap();
            m_methodAnnotationCache.put(method, annotationMap);
        }
        Object cachedAnnotation = annotationMap.get(annotationName);
        if (cachedAnnotation != null) {
            return (Annotation)cachedAnnotation;
        }
        // not in cache - create a new DP and put in cache
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(method);
        final Map annotations = (Map)m_methodAnnotationElements.get(key);
        if (annotations == null) {
            // no such annotation
            return null;
        }
        Object annotationElement = annotations.get(annotationName);
        if (annotationElement != null) {
            Annotation annotation = ProxyFactory.newAnnotationProxy(
                    (AnnotationElement.Annotation)annotationElement,
                    method.getDeclaringClass().getClassLoader()
            );
            annotationMap.put(annotationName, annotation);
            return annotation;
        }
        return null;
    }

    /**
     * Returns all the method annotations.
     *
     * @param method the java.lang.reflect.Method object to find the annotations on.
     * @return an array with the method annotations
     */
    public Annotation[] getAnnotations(final Method method) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(method);
        Object map = m_methodAnnotationElements.get(key);
        if (map != null) {
            final Collection annotationElements = ((Map)map).values();
            if (annotationElements.isEmpty()) {
                return EMPTY_ANNOTATION_ARRAY;
            }
            final Map cache = getAnnotationCacheFor(method);
            return getAnnotations(annotationElements, cache);
        } else {
            return EMPTY_ANNOTATION_ARRAY;
        }
    }

    /**
     * Checks if an annotation is present at a specific field.
     *
     * @param annotationType the annotation type
     * @return true if the annotation is present else false
     */
    public boolean isAnnotationPresent(final Class annotationType, final Field field) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(field);
        Object map = m_fieldAnnotationElements.get(key);
        if (map != null) {
            if (((Map)map).containsKey(annotationType.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the field annotation with the name specified for the field specified.
     *
     * @param annotationName
     * @return the field annotation
     */
    public Annotation getAnnotation(final String annotationName, final Field field) {
        Map annotationMap = (Map)m_fieldAnnotationCache.get(field);
        if (annotationMap == null) {
            annotationMap = new HashMap();
            m_fieldAnnotationCache.put(field, annotationMap);
        }
        Object cachedAnnotation = annotationMap.get(annotationName);
        if (cachedAnnotation != null) {
            return (Annotation)cachedAnnotation;
        }
         // not in cache - create a new DP and put in cache
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(field);
        final Map annotations = (Map)m_fieldAnnotationElements.get(key);
        if (annotations == null) {
            // no such annotation
            return null;
        }
        Object annotationElement = annotations.get(annotationName);
        if (annotationElement != null) {
            Annotation annotation = ProxyFactory.newAnnotationProxy(
                    (AnnotationElement.Annotation)annotationElement,
                    field.getDeclaringClass().getClassLoader()
            );
            annotationMap.put(annotationName, annotation);
            return annotation;
        }
        return null;
    }

    /**
     * Returns all the field annotations.
     *
     * @param field the java.lang.reflect.Field object to find the annotations on.
     * @return an array with the field annotations
     */
    public Annotation[] getAnnotations(final Field field) {
        final AnnotationReader.MemberKey key = AnnotationReader.MemberKey.newMemberKey(field);
        Object map = m_fieldAnnotationElements.get(key);
        if (map != null) {
            final Collection annotationElements = ((Map)map).values();
            if (annotationElements.isEmpty()) {
                return EMPTY_ANNOTATION_ARRAY;
            }
            final Map cache = getAnnotationCacheFor(field);
            return getAnnotations(annotationElements, cache);
        } else {
            return EMPTY_ANNOTATION_ARRAY;
        }
    }

    /**
     * Returns the annotations for a specific member.
     *
     * @param annotationElements the annotation elements for the member
     * @param annotationCache    the annotation cache to use
     * @return an array with the annotations
     */
    private Annotation[] getAnnotations(final Collection annotationElements, final Map annotationCache) {
        final Annotation[] annotations = new Annotation[annotationElements.size()];
        int i = 0;
        if (annotationCache.size() == annotationElements.size()) {
            // all are cached - use cache
            for (Iterator it = annotationCache.values().iterator(); it.hasNext();) {
                annotations[i++] = (Annotation)it.next();
            }
            return annotations;
        } else {
            // cache not complete - fill it up with the ones missing
            ClassLoader loader = ((Class)m_classRef.get()).getClassLoader();
            i = 0;
            for (Iterator it = annotationElements.iterator(); it.hasNext();) {
                AnnotationElement.Annotation annotationElement = (AnnotationElement.Annotation)it.next();
                String annotationClassName = annotationElement.getInterfaceName();
                if (annotationCache.containsKey(annotationClassName)) {
                    // proxy in cache - reuse
                    annotations[i++] = (Annotation)annotationCache.get(annotationClassName);
                } else {
                    final Annotation annotation = ProxyFactory.newAnnotationProxy(annotationElement, loader);
                    annotationCache.put(annotationClassName, annotation);
                    annotations[i++] = annotation;
                }
            }
            return annotations;
        }
    }

    /**
     * Returns the annotation cache for a specific constructor.
     *
     * @param constructor the constructor
     * @return the cache
     */
    private Map getAnnotationCacheFor(final Constructor constructor) {
        Map annotationMap = (Map)m_constructorAnnotationCache.get(constructor);
        if (annotationMap == null) {
            annotationMap = new HashMap();
            m_constructorAnnotationCache.put(constructor, annotationMap);
        }
        return annotationMap;
    }

    /**
     * Returns the annotation cache for a specific method.
     *
     * @param method the method
     * @return the cache
     */
    private Map getAnnotationCacheFor(final Method method) {
        Map annotationMap = (Map)m_methodAnnotationCache.get(method);
        if (annotationMap == null) {
            annotationMap = new HashMap();
            m_methodAnnotationCache.put(method, annotationMap);
        }
        return annotationMap;
    }

    /**
     * Returns the annotation cache for a specific field.
     *
     * @param field the field
     * @return the cache
     */
    private Map getAnnotationCacheFor(final Field field) {
        Map annotationMap = (Map)m_fieldAnnotationCache.get(field);
        if (annotationMap == null) {
            annotationMap = new HashMap();
            m_fieldAnnotationCache.put(field, annotationMap);
        }
        return annotationMap;
    }

    /**
     * Resets the annotation reader and triggers a new parsing of the newly read bytecode.
     * <p/>
     * This method calls <code>parse</code> and is therefore all the is needed to invoke to get a fully updated reader.
     */
    private void refresh() {
        m_classAnnotationElements.clear();
        m_constructorAnnotationElements.clear();
        m_methodAnnotationElements.clear();
        m_fieldAnnotationElements.clear();
        m_classAnnotationCache.clear();
        m_constructorAnnotationCache.clear();
        m_methodAnnotationCache.clear();
        m_fieldAnnotationCache.clear();
        parse((Class)m_classRef.get());
    }

    /**
     * Parses the class bytecode and retrieves the annotations.
     *
     * @param klass
     */
    private void parse(final Class klass) {
        final String className = klass.getName();
        final ClassLoader loader = klass.getClassLoader();
        final byte[] bytes;
        try {
            bytes = BYTECODE_PROVIDER.getBytecode(className, loader);
        } catch (Exception e) {
            throw new ReaderException("could not retrieve the bytecode from the bytecode provider [" + BYTECODE_PROVIDER.getClass().getName() + "]", e);
        }
        ClassReader classReader = new ClassReader(bytes);
        ClassWriter writer = new ClassWriter(true);
        classReader.accept(new AnnotationRetrievingVisitor(writer), false);
    }

    /**
     * Creates a new instance of the annotation reader, reads from the class specified.
     *
     * @param klass
     */
    private AnnotationReader(final Class klass) {
        if (klass == null) {
            throw new IllegalArgumentException("class can not be null");
        }
        m_classRef = new WeakReference(klass);
        parse(klass);
    }

    /**
     * Retrieves the Java 5 RuntimeVisibleAnnotations annotations from the class bytecode.
     *
     * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
     */
    private class AnnotationRetrievingVisitor extends ClassAdapter {

        public AnnotationRetrievingVisitor(final ClassVisitor cv) {
            super(cv);
        }

        public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
            cv.visitAnnotation(desc, visible);
            String annotationClassName = toJavaName(desc);
            final AnnotationElement.Annotation annotation = new AnnotationElement.Annotation(annotationClassName);
            m_classAnnotationElements.put(annotationClassName, annotation);
            return createAnnotationVisitor(annotation);
        }

        public FieldVisitor visitField(
                final int access,
                final String name,
                final String desc,
                final String signature,
                final Object value) {
            final FieldVisitor visitor = cv.visitField(access, name, desc, signature, value);

            final MemberKey key = new MemberKey(name, desc);
            return new FieldVisitor() {
                public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
                    final String className = toJavaName(desc);
                    final AnnotationElement.Annotation annotation = new AnnotationElement.Annotation(className);
                    if (m_fieldAnnotationElements.containsKey(key)) {
                        ((Map)m_fieldAnnotationElements.get(key)).put(className, annotation);
                    } else {
                        final Map annotations = new HashMap();
                        annotations.put(className, annotation);
                        m_fieldAnnotationElements.put(key, annotations);
                    }
                    return createAnnotationVisitor(annotation);
                }

                public void visitAttribute(final Attribute attribute) {
                    visitor.visitAttribute(attribute);
                }

                public void visitEnd() {
                    visitor.visitEnd();
                }
            };
        }

        public MethodVisitor visitMethod(
                final int access,
                final String name,
                final String desc,
                final String signature,
                final String[] exceptions) {
            MethodVisitor visitor = cv.visitMethod(access, name, desc, signature, exceptions);

            final MemberKey key = new MemberKey(name, desc);
            if (name.equals(INIT_METHOD_NAME)) {
                return new MethodAdapter(visitor) {
                    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
                        final String className = toJavaName(desc);
                        final AnnotationElement.Annotation annotation = new AnnotationElement.Annotation(className);
                        if (m_constructorAnnotationElements.containsKey(key)) {
                            ((Map)m_constructorAnnotationElements.get(key)).put(className, annotation);
                        } else {
                            final Map annotations = new HashMap();
                            annotations.put(className, annotation);
                            m_constructorAnnotationElements.put(key, annotations);
                        }
                        return createAnnotationVisitor(annotation);
                    }
                };

            } else {
                return new MethodAdapter(visitor) {
                    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
                        String className = toJavaName(desc);
                        final AnnotationElement.Annotation annotation = new AnnotationElement.Annotation(className);
                        if (m_methodAnnotationElements.containsKey(key)) {
                            ((Map)m_methodAnnotationElements.get(key)).put(className, annotation);
                        } else {
                            final Map annotations = new HashMap();
                            annotations.put(className, annotation);
                            m_methodAnnotationElements.put(key, annotations);
                        }
                        return createAnnotationVisitor(annotation);
                    }
                };
            }
        }

        /**
         * Returns the annotation visitor to use.
         * <p/>
         * Swap to the 'tracing' visitor for simple debugging.
         *
         * @param annotation
         *
         * @return
         */
        public AnnotationVisitor createAnnotationVisitor(final AnnotationElement.Annotation annotation) {
            return new AnnotationBuilderVisitor(annotation);
//            return new TraceAnnotationVisitor();
        }
    }

    static class AnnotationBuilderVisitor implements AnnotationVisitor {

        private final AnnotationElement.NestedAnnotationElement m_nestedAnnotationElement;

        public AnnotationBuilderVisitor(final AnnotationElement.NestedAnnotationElement annotation) {
            m_nestedAnnotationElement = annotation;
        }

        public void visit(final String name, final Object value) {
            if (value instanceof Type) {
                // type
                m_nestedAnnotationElement.addElement(name, value);
            } else {
                // primitive value
                if (value.getClass().isArray()) {
                    // primitive array value
                    handlePrimitiveArrayValue(value, name);
                } else {
                    // primitive non-array value
                    m_nestedAnnotationElement.addElement(name, value);
                }
            }
        }

        public void visitEnum(final String name, final String desc, final String value) {
            m_nestedAnnotationElement.addElement(name, new AnnotationElement.Enum(desc, value));
        }

        public AnnotationVisitor visitAnnotation(final String name, final String desc) {
            String className = toJavaName(desc);
            AnnotationElement.NestedAnnotationElement annotation = new AnnotationElement.Annotation(className);
            m_nestedAnnotationElement.addElement(name, annotation);
            return new AnnotationBuilderVisitor(annotation);
        }

        public AnnotationVisitor visitArray(final String name) {
            AnnotationElement.NestedAnnotationElement array = new AnnotationElement.Array();
            m_nestedAnnotationElement.addElement(name, array);
            return new AnnotationBuilderVisitor(array);
        }

        public void visitEnd() {
        }

        /**
         * Handles array of primitive values. The JSR-175 spec. only suppots one dimensional arrays.
         *
         * @param value
         * @param name
         */
        private void handlePrimitiveArrayValue(final Object value, final String name) {
            if (value.getClass().getComponentType().isPrimitive()) {
                // primitive array type
                if (value instanceof String[]) {
                    // string array
                    m_nestedAnnotationElement.addElement(name, value);
                } else {
                    AnnotationElement.NestedAnnotationElement arrayElement = new AnnotationElement.Array();
                    // non-string primitive array
                    if (value instanceof int[]) {
                        int[] array = (int[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Integer(array[i]));
                        }
                    } else if (value instanceof long[]) {
                        long[] array = (long[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Long(array[i]));
                        }
                    } else if (value instanceof short[]) {
                        short[] array = (short[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Short(array[i]));
                        }
                    } else if (value instanceof float[]) {
                        float[] array = (float[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Float(array[i]));
                        }
                    } else if (value instanceof double[]) {
                        double[] array = (double[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Double(array[i]));
                        }
                    } else if (value instanceof boolean[]) {
                        boolean[] array = (boolean[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Boolean(array[i]));
                        }
                    } else if (value instanceof byte[]) {
                        byte[] array = (byte[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Byte(array[i]));
                        }
                    } else if (value instanceof char[]) {
                        char[] array = (char[])value;
                        for (int i = 0; i < array.length; i++) {
                            arrayElement.addElement(null, new Character(array[i]));
                        }
                    }
                    m_nestedAnnotationElement.addElement(name, arrayElement);
                }
            } else {
                m_nestedAnnotationElement.addElement(name, value);
            }
        }
    }

    /**
     * Unique key for class members (methods, fields and constructors) to be used in hash maps etc.
     * <p/>
     * Needed since at bytecode parsing time we do not have access to the reflect members, only strings.
     *
     * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
     */
    public static class MemberKey {
        private final String m_name;
        private final String m_desc;

        public static MemberKey newMemberKey(final Constructor method) {
            return new MemberKey(INIT_METHOD_NAME, SignatureHelper.getConstructorSignature(method));
        }

        public static MemberKey newMemberKey(final Method method) {
            return new MemberKey(method.getName(), SignatureHelper.getMethodSignature(method));
        }

        public static MemberKey newMemberKey(final Field field) {
            return new MemberKey(field.getName(), SignatureHelper.getFieldSignature(field));
        }

        public MemberKey(final String name, final String desc) {
            m_name = name;
            m_desc = desc;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof MemberKey)) {
                return false;
            }

            final MemberKey memberKey = (MemberKey)o;

            if (m_desc != null ? !m_desc.equals(memberKey.m_desc) : memberKey.m_desc != null) {
                return false;
            }
            if (m_name != null ? !m_name.equals(memberKey.m_name) : memberKey.m_name != null) {
                return false;
            }

            return true;
        }

        public int hashCode() {
            int result;
            result = (m_name != null ? m_name.hashCode() : 0);
            result = 29 * result + (m_desc != null ? m_desc.hashCode() : 0);
            return result;
        }
    }

    /**
     * To be used for debugging purposes.
     */
    private class TraceAnnotationVisitor implements AnnotationVisitor {
        public void visit(final String name, final Object value) {
            System.out.println("    NAMED-VALUE: " + name + "->" + value);
        }

        public void visitEnum(final String name, final String desc, final String value) {
            System.out.println("    ENUM: " + name);
        }

        public AnnotationVisitor visitAnnotation(final String name, final String desc) {
            System.out.println("    ANNOTATION: " + name);
            return new TraceAnnotationVisitor();
        }

        public AnnotationVisitor visitArray(final String name) {
            System.out.println("    ARRAY: " + name);
            return new TraceAnnotationVisitor();
        }

        public void visitEnd() {
        }
    }
}
TOP

Related Classes of org.codehaus.backport175.reader.bytecode.AnnotationReader$MemberKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.