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