/* | |
* 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); | |
} | |
} | |
} |