blob: 71b383785b08d1f6fb72371230fcc5e9d8e11e6f [file] [log] [blame]
/*
* 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 {
// Avoid 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;
}
}
/**
* 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 visit(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();
}
/**
* 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 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 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);
}
}
}