org.apache.felix.ipojo.manipulation.ClassChecker.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.felix.ipojo.manipulation.ClassChecker.java

Source

/* 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.felix.ipojo.manipulation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;

/**
 * Checks that a POJO is already manipulated or not.
 * Moreover it allows to get manipulation data about this class. 
 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
 */
public class ClassChecker extends EmptyVisitor implements ClassVisitor, Opcodes {

    /**
     * True if the class is already manipulated.
     */
    private boolean m_isAlreadyManipulated = false;

    /**
     * Interfaces implemented by the component.
     */
    private List m_itfs = new ArrayList();

    /**
     * Field map [field name, type] discovered in the component class.
     */
    private Map m_fields = new HashMap();

    /**
     * Method List of method descriptor discovered in the component class.
     */
    private List m_methods = new ArrayList()/* <MethodDesciptor> */;

    /**
     * Super class if not java.lang.Object.
     */
    private String m_superClass;

    /**
     * Class name.
     */
    private String m_className;

    /**
     * List of visited inner class owned by the implementation class.
     */
    private List m_inners = new ArrayList();

    /**
     * <code>true</code> if the class supports annotations.
     * This enables the analysis of the code to find and moves annotations.
     */
    private boolean m_supportAnnotation = false;

    /**
     * Check if the _cm field already exists.
     * Update the field list.
     * @param access : access of the field
     * @param name : name of the field
     * @param desc : description of the field
     * @param signature : signature of the field
     * @param value : value of the field (for static field only)
     * @return the field visitor
     * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
     */
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {

        if (access == ACC_PRIVATE && name.equals(MethodCreator.IM_FIELD)
                && desc.equals("Lorg/apache/felix/ipojo/InstanceManager;")) {
            m_isAlreadyManipulated = true;
        } else if (name.startsWith("class$")) { // Does not add class$* field generated by 'x.class'
            return null;
        } else if ((access & ACC_STATIC) == ACC_STATIC) {
            return null;
        }

        Type type = Type.getType(desc);
        if (type.getSort() == Type.ARRAY) {
            if (type.getInternalName().startsWith("L")) {
                String internalType = type.getInternalName().substring(1);
                String nameType = internalType.replace('/', '.');
                m_fields.put(name, nameType + "[]");
            } else {
                String nameType = type.getClassName().substring(0, type.getClassName().length() - 2);
                m_fields.put(name, nameType + "[]");
            }
        } else {
            m_fields.put(name, type.getClassName());
        }

        return null;
    }

    /**
     * Add the inner class to the list of inner class to manipulate.
     * The method checks that the inner class is really owned by the implementation class.
     * @param name inner class qualified name
     * @param outerName outer class name (may be null for anonymous class)
     * @param innerName inner class simple (i.e. short) name
     * @param access inner class visibility
     * @see org.objectweb.asm.commons.EmptyVisitor#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
     */
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        if (m_className.equals(outerName) || outerName == null) { // Anonymous classes does not have an outer class.
            m_inners.add(name);
        }
    }

    /**
     * Check if the class was already manipulated.
     * @return true if the class is already manipulated.
     */
    public boolean isalreadyManipulated() {
        return m_isAlreadyManipulated;
    }

    /**
     * Visit the class.
     * Update the implemented interface list.
     * @param version : version of the class
     * @param access : access of the class
     * @param name : name of the class
     * @param signature : signature of the class
     * @param superName : super class of the class
     * @param interfaces : implemented interfaces.
     * @see org.objectweb.asm.ClassVisitor#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {

        m_supportAnnotation = version > V1_4 && version < V1_1;

        if (!superName.equals("java/lang/Object")) {
            m_superClass = superName.replace('/', '.');
        }

        for (int i = 0; i < interfaces.length; i++) {
            if (!interfaces[i].equals("org/apache/felix/ipojo/Pojo")) {
                m_itfs.add(interfaces[i].replace('/', '.'));
            }
        }

        m_className = name;
    }

    /**
     * Visit a method.
     * Update the method list (except if it init or clinit.
     * @param  access - the method's access flags (see Opcodes). This parameter also indicates if the method is synthetic and/or deprecated.
     * @param name - the method's name.
     * @param desc - the method's descriptor (see Type).
     * @param signature - the method's signature. May be null if the method parameters, return type and exceptions do not use generic types.
     * @param exceptions - the internal names of the method's exception classes (see getInternalName). May be null.
     * @return nothing.
     * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (!name.equals("<clinit>")) {

            if (name.equals("<init>")) {
                final MethodDescriptor md = new MethodDescriptor("$init", desc);
                m_methods.add(md);
                if (m_supportAnnotation) {
                    return new AnnotationCollector(md);
                }
            } else {
                // no constructors.
                if (!(name.startsWith("_get") || // Avoid getter method
                        name.startsWith("_set") || // Avoid setter method
                        name.equals("_setComponentManager") || // Avoid the set method
                        name.equals("getComponentInstance"))) { // Avoid the getComponentInstance method
                    final MethodDescriptor md = new MethodDescriptor(name, desc);
                    m_methods.add(md);
                    if (m_supportAnnotation) {
                        return new AnnotationCollector(md);
                    }
                }
            }

        }

        return null;
    }

    /**
     * Get collected interfaces.
     * @return the interfaces implemented by the component class.
     */
    public List getInterfaces() {
        return m_itfs;
    }

    /**
     * Get collected fields.
     * @return the field map [field_name, type].
     */
    public Map getFields() {
        return m_fields;
    }

    /**
     * Get collected methods.
     * @return the method list of [method, signature].
     */
    public List getMethods() {
        return m_methods;
    }

    public String getSuperClass() {
        return m_superClass;
    }

    public List getInnerClasses() {
        return m_inners;
    }

    /**
     * This class collects annotations in a method.
     * This class creates an {@link AnnotationDescriptor} 
     * if an annotation is found during the visit.
     *  @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    private final class AnnotationCollector extends EmptyVisitor {
        /**
         * The method descriptor of the visited method.
         */
        private MethodDescriptor m_method;

        /**
         * Creates an annotation collector.
         * @param md the method descriptor of the visited method.
         */
        private AnnotationCollector(MethodDescriptor md) {
            m_method = md;
        }

        /**
         * Visits an annotation.
         * This class checks the visibility. If the annotation is visible,
         * creates the {@link AnnotationDescriptor} corresponding to this annotation
         * to visit this annotation. This {@link AnnotationDescriptor} is added to
         * the {@link MethodDescriptor} of the visited method.
         * @param name the name of the annotation
         * @param visible is the annotation visible at runtime
         * @return the {@link AnnotationDescriptor} to visit this annotation or
         * <code>null</code> if the annotation is not visible.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitAnnotation(java.lang.String, boolean)
         */
        public AnnotationVisitor visitAnnotation(String name, boolean visible) {
            if (visible) {
                AnnotationDescriptor ann = new AnnotationDescriptor(name, visible);
                m_method.addAnnotation(ann);
                return ann;
            }
            return null;
        }

        public AnnotationVisitor visitParameterAnnotation(int id, String name, boolean visible) {
            if (visible) {
                AnnotationDescriptor ann = new AnnotationDescriptor(name, visible);
                m_method.addParameterAnnotation(id, ann);
                return ann;
            }
            return null;
        }

    }

    /**
     * Describes a method or constructor annotation.
     * This allows creating a copy of the annotations found in the original class
     * to move them on inserted method. This class implements an
     * {@link AnnotationVisitor} in order to create the copy.
     * This class contains a <code>visit</code> method re-injecting the
     * annotation in the generated method.
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    public class AnnotationDescriptor implements AnnotationVisitor {
        /**
         * The name of the annotation.
         */
        private String m_name;
        /**
         * Is the annotation visible at runtime?
         */
        private boolean m_visible;
        /**
         * The description of the annotation.
         * This attribute is set only for nested annotations.
         */
        private String m_desc;
        /**
         * The list of 'simple' attributes.
         */
        private List m_simples = new ArrayList(0);
        /**
         * The list of attribute containing an
         * enumeration value.
         */
        private List m_enums = new ArrayList(0);
        /**
         * The list of attribute which are
         * annotations.
         */
        private List m_nested = new ArrayList(0);
        /**
         * The list of attribute which are
         * arrays. 
         */
        private List m_arrays = new ArrayList(0);

        /**
         * Creates an annotation descriptor.
         * This constructor is used for 'root' annotations.
         * @param name the name of the  annotation
         * @param visible the visibility of the annotation at runtime
         */
        public AnnotationDescriptor(String name, boolean visible) {
            m_name = name;
            m_visible = visible;
        }

        /**
         * Creates an annotation descriptor.
         * This constructor is used for nested annotations.
         * @param name the name of the  annotation
         * @param desc the descriptor of the annotation
         */
        public AnnotationDescriptor(String name, String desc) {
            m_name = name;
            m_visible = true;
            m_desc = desc;
        }

        /**
         * Visits a simple attribute.
         * @param arg0 the attribute name
         * @param arg1 the attribute value
         * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            m_simples.add(new SimpleAttribute(arg0, arg1));
        }

        /**
         * Visits a nested annotation.
         * @param arg0 the attribute name
         * @param arg1 the annotation descriptor
         * @return the annotation visitor parsing the nested annotation
         * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
         */
        public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
            AnnotationDescriptor ad = new AnnotationDescriptor(arg0, arg1);
            m_nested.add(ad);
            return ad;
        }

        /**
         * Visits an array attribute.
         * @param arg0 the name of the attribute
         * @return the annotation visitor parsing the content of the array,
         * uses a specific {@link ArrayAttribute} to parse this array
         * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
         */
        public AnnotationVisitor visitArray(String arg0) {
            ArrayAttribute aa = new ArrayAttribute(arg0);
            m_arrays.add(aa);
            return aa;
        }

        /**
         * End of the visit.
         * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
         */
        public void visitEnd() {
        }

        /**
         * Visits an enumeration attribute.
         * @param arg0 the attribute name
         * @param arg1 the enumeration descriptor
         * @param arg2 the attribute value
         * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
         */
        public void visitEnum(String arg0, String arg1, String arg2) {
            m_enums.add(new EnumAttribute(arg0, arg1, arg2));
        }

        /**
         * Methods allowing to recreate the visited (stored) annotation
         * into the destination method.
         * This method recreate the annotations itself and any other 
         * attributes.
         * @param mv the method visitor visiting the destination method.
         */
        public void visitAnnotation(MethodVisitor mv) {
            AnnotationVisitor av = mv.visitAnnotation(m_name, m_visible);
            for (int i = 0; i < m_simples.size(); i++) {
                ((SimpleAttribute) m_simples.get(i)).visit(av);
            }
            for (int i = 0; i < m_enums.size(); i++) {
                ((EnumAttribute) m_enums.get(i)).visit(av);
            }
            for (int i = 0; i < m_nested.size(); i++) {
                ((AnnotationDescriptor) m_nested.get(i)).visit(av);
            }
            for (int i = 0; i < m_arrays.size(); i++) {
                ((ArrayAttribute) m_arrays.get(i)).visit(av);
            }
            av.visitEnd();
        }

        /**
         * Methods allowing to recreate the visited (stored) parameter annotations
         * into the destination method.
         * This method recreate the annotations itself and any other 
         * attributes.
         * @param id the paramter id
         * @param mv the method visitor visiting the destination method.
         */
        public void visitParameterAnnotation(int id, MethodVisitor mv) {
            AnnotationVisitor av = mv.visitParameterAnnotation(id, m_name, m_visible);
            for (int i = 0; i < m_simples.size(); i++) {
                ((SimpleAttribute) m_simples.get(i)).visit(av);
            }
            for (int i = 0; i < m_enums.size(); i++) {
                ((EnumAttribute) m_enums.get(i)).visit(av);
            }
            for (int i = 0; i < m_nested.size(); i++) {
                ((AnnotationDescriptor) m_nested.get(i)).visit(av);
            }
            for (int i = 0; i < m_arrays.size(); i++) {
                ((ArrayAttribute) m_arrays.get(i)).visit(av);
            }
            av.visitEnd();
        }

        /**
         * Method allowing to recreate the visited (stored) annotation
         * into the destination annotation. This method is used only
         * for nested annotation.
         * @param mv the annotation visitor to populate with the stored
         * annotation
         */
        public void visit(AnnotationVisitor mv) {
            AnnotationVisitor av = mv.visitAnnotation(m_name, m_desc);
            for (int i = 0; i < m_simples.size(); i++) {
                ((SimpleAttribute) m_simples.get(i)).visit(av);
            }
            for (int i = 0; i < m_enums.size(); i++) {
                ((EnumAttribute) m_enums.get(i)).visit(av);
            }
            for (int i = 0; i < m_nested.size(); i++) {
                ((AnnotationDescriptor) m_nested.get(i)).visit(av);
            }
            for (int i = 0; i < m_arrays.size(); i++) {
                ((ArrayAttribute) m_arrays.get(i)).visit(av);
            }
            av.visitEnd();
        }

    }

    /**
     * Describes an array attribute.
     * This class is able to visit an annotation array attribute, and to
     * recreate this array on another annotation.
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    public class ArrayAttribute implements AnnotationVisitor {
        /**
         * The name of the attribute.
         */
        private String m_name;
        /**
         * The content of the parsed array.
         */
        private List m_content = new ArrayList();

        /**
         * Creates an array attribute.
         * @param name the name of the attribute.
         */
        public ArrayAttribute(String name) {
            m_name = name;
        }

        /**
         * Visits the content of the array. This method is called for 
         * simple values. 
         * @param arg0 <code>null</code>
         * @param arg1 the value
         * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            m_content.add(arg1);
        }

        /**
         * Visits the content of the array. This method is called for 
         * nested annotations (annotations contained in the array). 
         * @param arg0 <code>null</code>
         * @param arg1 the annotation descriptor
         * @return an {@link AnnotationDescriptor} which creates a copy of
         * the contained annotation.
         * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(String, String)
         */
        public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
            AnnotationDescriptor ad = new AnnotationDescriptor(null, arg1);
            m_content.add(ad);
            return ad;
        }

        /**
         * Visits the content of the array. This method is called for 
         * nested arrays (arrays contained in the array). 
         * @param arg0 <code>null</code>
         * @return an {@link ArrayDescriptor} which creates a copy of
         * the contained array.
         * @see org.objectweb.asm.AnnotationVisitor#visitArray(String)
         */
        public AnnotationVisitor visitArray(String arg0) {
            ArrayAttribute aa = new ArrayAttribute(null);
            m_content.add(aa);
            return aa;
        }

        /**
         * End of the array attribute visit.
         * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
         */
        public void visitEnd() {
        }

        /**
         * Visits the content of the array. This method is called for 
         * enumeration values. 
         * @param arg0 <code>null</code>
         * @param arg1 the enumeration descriptor
         * @param arg2 the value
         * @see org.objectweb.asm.AnnotationVisitor#visitEnum(String, String, String)
         */
        public void visitEnum(String arg0, String arg1, String arg2) {
            EnumAttribute ea = new EnumAttribute(null, arg1, arg2);
            m_content.add(ea);
        }

        /**
         * Recreates the visited array attribute. This method
         * handle the generation of the object embedded in the
         * array.
         * @param av the annotation visitor on which the array attribute
         * needs to be injected.
         */
        public void visit(AnnotationVisitor av) {
            AnnotationVisitor content = av.visitArray(m_name);
            for (int i = 0; i < m_content.size(); i++) {
                Object component = m_content.get(i);
                if (component instanceof AnnotationDescriptor) {
                    ((AnnotationDescriptor) component).visit(content);
                } else if (component instanceof EnumAttribute) {
                    ((EnumAttribute) component).visit(content);
                } else if (component instanceof ArrayAttribute) {
                    ((ArrayAttribute) component).visit(content);
                } else { // Simple
                    content.visit(null, component);
                }
            }
            content.visitEnd();
        }

    }

    /**
     * Describes a simple attribute.
     * This class is able to visit an annotation simple attribute, and to
     * recreate this attribute on another annotation.
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    public static final class SimpleAttribute {
        /**
         * The name of the attribute.
         */
        private String m_name;
        /**
         * The value of the attribute.
         */
        private Object m_value;

        /**
         * Creates a simple attribute.
         * @param name the name of the attribute
         * @param object the value of the attribute
         */
        private SimpleAttribute(String name, Object object) {
            m_name = name;
            m_value = object;
        }

        /**
         * Recreates the attribute on the given annotation.
         * @param visitor the visitor on which the attribute needs
         * to be injected.
         */
        public void visit(AnnotationVisitor visitor) {
            visitor.visit(m_name, m_value);
        }
    }

    /**
     * Describes an attribute. The value of this attribute is an enumerated
     * value.
     * This class is able to visit an annotation enumeration attribute, and to
     * recreate this attribute on another annotation.
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    public static final class EnumAttribute {
        /**
         * The name of the attribute.
         */
        private String m_name;
        /**
         * The descriptor of the enumeration.
         */
        private String m_desc;
        /**
         * The value of the attribute.
         */
        private String m_value;

        /**
         * Creates a enumeration attribute.
         * @param name the name of the attribute.
         * @param desc the descriptor of the {@link Enum}
         * @param value the enumerated value
         */
        private EnumAttribute(String name, String desc, String value) {
            m_name = name;
            m_value = value;
            m_desc = desc;
        }

        /**
         * Recreates the attribute on the given annotation.
         * @param visitor the visitor on which the attribute needs
         * to be injected.
         */
        public void visit(AnnotationVisitor visitor) {
            visitor.visitEnum(m_name, m_desc, m_value);
        }

    }

}