Add the new manipulator project (Felix-311)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@550247 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/pom.xml b/ipojo/manipulator/pom.xml
new file mode 100644
index 0000000..c965f08
--- /dev/null
+++ b/ipojo/manipulator/pom.xml
@@ -0,0 +1,21 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
+ <packaging>jar</packaging>
+ <version>0.7.3-incubator-SNAPSHOT</version>
+ <name>Apache Felix iPOJO Manipulator</name>
+ <dependencies>
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.apache.felix.ipojo.metadata</artifactId>
+ <version>0.7.3-incubator-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
new file mode 100644
index 0000000..0168055
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
@@ -0,0 +1,223 @@
+/*
+ * 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+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;
+
+/**
+ * Check thaht a POJO is already manipulated or not.
+ * Moreover it allows to get manipulation data about this class.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ClassChecker implements ClassVisitor, Opcodes {
+
+ /**
+ * True if the class is already manipulated.
+ */
+ private boolean m_isAlreadyManipulated = false;
+
+ /**
+ * Interfaces implemented by the component.
+ */
+ private String[] m_itfs = new String[0];
+
+ /**
+ * Field hashmap [field name, type] discovered in the component class.
+ */
+ private HashMap m_fields = new HashMap();
+
+ /**
+ * Method List of method descriptor discovered in the component class.
+ */
+ private List m_methods = new ArrayList()/* <MethodDesciptor> */;
+
+ /**
+ * 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("_cm")
+ && desc.equals("Lorg/apache/felix/ipojo/InstanceManager;")) {
+ m_isAlreadyManipulated = true;
+ }
+
+ 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;
+ }
+
+ /**
+ * 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 insterface list.
+ * @param version : version of the class
+ * @param access : acess 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) {
+ // Store the interfaces :
+ m_itfs = interfaces;
+ }
+
+ /**
+ * Visit sources.
+ * Do nothing
+ * @param source : the name of the source file from which the class was compiled. May be null.
+ @param debug : additional debug information to compute the correspondance between source and compiled elements of the class. May be null.
+ * @see org.objectweb.asm.ClassVisitor#visitSource(java.lang.String, java.lang.String)
+ */
+ public void visitSource(String source, String debug) { }
+
+ /**
+ * Visit an outer class.
+ * @param owner - internal name of the enclosing class of the class.
+ * @param name - the name of the method that contains the class, or null if the class is not enclosed in a method of its enclosing class.
+ * @param desc - the descriptor of the method that contains the class, or null if the class is not enclosed in a method of its enclosing class.
+ * @see org.objectweb.asm.ClassVisitor#visitOuterClass(java.lang.String,
+ * java.lang.String, java.lang.String)
+ */
+ public void visitOuterClass(String owner, String name, String desc) {
+ }
+
+ /**
+ * Visit an annotation.
+ * Do nothing.
+ * @param desc - the class descriptor of the annotation class.
+ * @param visible - true if the annotation is visible at runtime.
+ * @return null.
+ * @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean)
+ */
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return null;
+ }
+
+ /**
+ * Visit a non standard attribute of the class.
+ * @param attr - an attribute.
+ * @see org.objectweb.asm.ClassVisitor#visitAttribute(org.objectweb.asm.Attribute)
+ */
+ public void visitAttribute(Attribute attr) {
+ }
+
+ /**
+ * Visit an inner class.
+ * @param name - the internal name of an inner class (see getInternalName).
+ * @param outerName - the internal name of the class to which the inner class belongs (see getInternalName). May be null for not member classes.
+ * @param innerName - the (simple) name of the inner class inside its enclosing class. May be null for anonymous inner classes.
+ * @param access - the access flags of the inner class as originally declared in the enclosing class.
+ * @see org.objectweb.asm.ClassVisitor#visitInnerClass(java.lang.String,
+ * java.lang.String, java.lang.String, int)
+ */
+ public void visitInnerClass(String name, String outerName, String innerName, int access) { }
+
+ /**
+ * 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("<init>") && !name.equals("<clinit>")) {
+ m_methods.add(new MethodDescriptor(name, desc));
+ }
+ return null;
+ }
+
+ /**
+ * End of the class visit.
+ * @see org.objectweb.asm.ClassVisitor#visitEnd()
+ */
+ public void visitEnd() { }
+
+ /**
+ * Get collected interfaces.
+ * @return the interfaces implemented by the component class.
+ */
+ public String[] getInterfaces() {
+ return m_itfs;
+ }
+
+ /**
+ * Get collected fields.
+ * @return the field hashmap [field_name, type].
+ */
+ public HashMap getFields() {
+ return m_fields;
+ }
+
+ /**
+ * Get collected methods.
+ * @return the method list of [method, signature].
+ */
+ public List getMethods() {
+ return m_methods;
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java
new file mode 100644
index 0000000..29ab001
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java
@@ -0,0 +1,166 @@
+/*
+ * 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 org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+/**
+ * Constructor Adapter : add a component manager argument inside a constructor.
+ *
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ConstructorCodeAdapter extends GeneratorAdapter implements Opcodes {
+
+ /**
+ * The owner class of the field.
+ * m_owner : String
+ */
+ private String m_owner;
+
+ /**
+ * Is the super call already detected ?
+ */
+ private boolean m_superDetected;
+
+ /**
+ * ConstructorCodeAdapter constructor.
+ * @param mv : method visitor.
+ * @param access : access level of the method.
+ * @param desc : descriptor of the constructor.
+ * @param owner : onwer class
+ */
+ public ConstructorCodeAdapter(final MethodVisitor mv, int access,
+ String desc, final String owner) {
+ super(mv, access, "<init>", desc);
+ m_owner = owner;
+ // m_superDetected = false;
+ }
+
+ /**
+ * Visit a method instruction.
+ * Inject the _setComponentManager invocation just of the super invocation.
+ * @param opcode : opcode
+ * @param owner : method owner class
+ * @param name : method name
+ * @param desc : method description
+ * @see org.objectweb.asm.commons.AdviceAdapter#visitMethodInsn(int, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ // A method call is detected, check if it is the super call :
+ if (!m_superDetected) {
+ m_superDetected = true;
+ // The first invocation is the super call
+ // 1) Visit the super constructor :
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(opcode, owner, name, desc); // Super constructor
+ // invocation
+
+ // 2) Load the object and the component manager argument
+ mv.visitVarInsn(ALOAD, 0);
+ // mv.visitVarInsn(ALOAD,
+ // Type.getArgumentTypes(m_constructorDesc).length);
+ mv.visitVarInsn(ALOAD, 1); // CM is always the first argument
+ // 3) Initialize the field
+ mv.visitMethodInsn(INVOKESPECIAL, m_owner, "_setComponentManager",
+ "(Lorg/apache/felix/ipojo/InstanceManager;)V");
+ // insertion finished
+ } else {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+ }
+
+ /**
+ * Visit an instruction modifying a local variable.
+ * Increment the variable number if != 0.
+ * @param opcode : opcode of the instruction.
+ * @param var : variable number.
+ * @see org.objectweb.asm.commons.AdviceAdapter#visitVarInsn(int, int)
+ */
+ public void visitVarInsn(int opcode, int var) {
+ if (var == 0) {
+ mv.visitVarInsn(opcode, var); // ALOAD 0 (THIS)
+ } else {
+ mv.visitVarInsn(opcode, var + 1); // All other variable count
+ }
+ }
+
+ /**
+ * Visit an increment instruction.
+ * Increment the variable number.
+ * @param var : incremented variable number.
+ * @param increment : increment.
+ * @see org.objectweb.asm.commons.LocalVariablesSorter#visitIincInsn(int, int)
+ */
+ public void visitIincInsn(int var, int increment) {
+ if (var != 0) {
+ mv.visitIincInsn(var + 1, increment);
+ } else {
+ mv.visitIincInsn(var, increment);
+ }
+ }
+
+ /**
+ * Visit local variable.
+ * @param name : name of the variable.
+ * @param desc : description of the variable.
+ * @param signature : signature of the variable.
+ * @param start : starting label.
+ * @param end : ending label.
+ * @param index : variable index.
+ * @see org.objectweb.asm.commons.LocalVariablesSorter#visitLocalVariable(java.lang.String, java.lang.String, java.lang.String, org.objectweb.asm.Label, org.objectweb.asm.Label, int)
+ */
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ if (index == 0) {
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+ mv.visitLocalVariable("_manager", "Lorg/apache/felix/ipojo/InstanceManager;", null, start, end, 1);
+ } else {
+ mv.visitLocalVariable(name, desc, signature, start, end, index + 1);
+ }
+ }
+
+ /**
+ * Manage a field operation.
+ * Replace GETFIELD and PUTFILED by getter and setter invocation.
+ *
+ * @param opcode : visited operation code
+ * @param owner : owner of the field
+ * @param name : name of the field
+ * @param desc : decriptor of the field
+ * @see org.objectweb.asm.commons.AdviceAdapter#visitFieldInsn(int, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void visitFieldInsn(final int opcode, final String owner,
+ final String name, final String desc) {
+ if (owner.equals(m_owner)) {
+ if (opcode == GETFIELD) {
+ String gDesc = "()" + desc;
+ visitMethodInsn(INVOKEVIRTUAL, owner, "_get" + name, gDesc);
+ return;
+ } else if (opcode == PUTFIELD) {
+ String sDesc = "(" + desc + ")V";
+ visitMethodInsn(INVOKESPECIAL, owner, "_set" + name, sDesc);
+ return;
+ }
+ }
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/FieldAdapter.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/FieldAdapter.java
new file mode 100644
index 0000000..cd6e278
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/FieldAdapter.java
@@ -0,0 +1,656 @@
+/*
+ * 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.logging.Level;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Create getter and setter for each fields .
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class FieldAdapter extends ClassAdapter implements Opcodes {
+
+ /**
+ * The owner.
+ * m_owner : String
+ */
+ private String m_owner;
+
+ /**
+ * Constructor.
+ * @param cv : Class visitor
+ */
+ public FieldAdapter(final ClassVisitor cv) {
+ super(cv);
+ }
+
+ /**
+ * The visit method. - Insert the _cm field - Create the _initialize method - Create the _cm setter method
+ *
+ * @see org.objectweb.asm.ClassVisitor#visit(int, int, String, String, String, String[])
+ * @param version : Version
+ * @param access : Access modifier
+ * @param name : name of the visited element
+ * @param signature : signature of the visited element
+ * @param superName : superclasses (extend clause)
+ * @param interfaces : implement clause
+ */
+ public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
+ m_owner = name;
+ }
+
+ /**
+ * Visit a Field.
+ * Inject the getter and the setter method for this field.
+ * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
+ * @param access : acces modifier
+ * @param name : name of the field
+ * @param desc : description of the field
+ * @param signature : signature of the field
+ * @param value : value of the field
+ * @return FieldVisitor : null
+ */
+ public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
+
+ if ((access & ACC_STATIC) == 0) {
+ ManipulationProperty.getLogger().log(Level.INFO, "Manipulate the field declaration of " + name);
+ Type type = Type.getType(desc);
+
+ if (type.getSort() == Type.ARRAY) {
+ String gDesc = "()" + desc;
+ createArrayGetter(name, gDesc, type);
+
+ // Generates setter method
+ String sDesc = "(" + desc + ")V";
+ createArraySetter(name, sDesc, type);
+
+ } else {
+ // Generate the getter method
+ String gDesc = "()" + desc;
+ createSimpleGetter(name, gDesc, type);
+
+ // Generates setter method
+ String sDesc = "(" + desc + ")V";
+ createSimpleSetter(name, sDesc, type);
+ }
+
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a getter method for an array.
+ * @param name : field name
+ * @param desc : method description
+ * @param type : contained type (inside the array)
+ */
+ private void createArraySetter(String name, String desc, Type type) {
+ MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "_set" + name, desc, null, null);
+
+ String internalType = desc.substring(1);
+ internalType = internalType.substring(0, internalType.length() - 2);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, name, internalType);
+
+ Label l1 = new Label();
+ mv.visitLabel(l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l2 = new Label();
+ mv.visitJumpInsn(IFNE, l2);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(l2);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ mv.visitInsn(RETURN);
+
+ // End
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Create a setter method for an array.
+ * @param name : field name
+ * @param desc : method description
+ * @param type : contained type (inside the array)
+ */
+ private void createArrayGetter(String name, String desc, Type type) {
+
+ String methodName = "_get" + name;
+ MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, methodName, desc, null, null);
+
+ String internalType = desc.substring(2);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l1 = new Label();
+ mv.visitJumpInsn(IFNE, l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalType);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ // mv.visitFieldInsn(GETFIELD, m_owner, name, "["+type.getInternalName()+";");
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalType);
+ mv.visitVarInsn(ASTORE, 1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 2);
+
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, internalType);
+ mv.visitVarInsn(ASTORE, 3);
+
+ Label l3a = new Label();
+ mv.visitLabel(l3a);
+
+ mv.visitVarInsn(ALOAD, 1);
+ Label l4a = new Label();
+ mv.visitJumpInsn(IFNULL, l4a);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
+
+ Label l5a = new Label();
+ mv.visitJumpInsn(IFNE, l5a);
+ mv.visitLabel(l4a);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalType + ")V");
+ mv.visitLabel(l5a);
+
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ARETURN);
+
+ // End
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Create the getter for a field.
+ * @param name : field of the dependency
+ * @param desc : description of the getter method
+ * @param type : type to return
+ */
+ private void createSimpleGetter(String name, String desc, Type type) {
+
+ String methodName = "_get" + name;
+ MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, methodName, desc, null, null);
+
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+
+ String internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ String boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+ String unboxingMethod = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][2];
+
+ Label l0 = new Label();
+ mv.visitLabel(l0);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l1 = new Label();
+ mv.visitJumpInsn(IFNE, l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitInsn(IRETURN);
+
+ mv.visitLabel(l1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitVarInsn(type.getOpcode(ISTORE), 1);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 2);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 3);
+
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitTypeInsn(CHECKCAST, boxingType);
+ mv.visitVarInsn(ASTORE, 4);
+
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitMethodInsn(INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + internalName);
+ mv.visitVarInsn(type.getOpcode(ISTORE), 5);
+
+ Label l5 = new Label();
+ mv.visitLabel(l5);
+
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 5);
+ Label l6 = new Label();
+ mv.visitJumpInsn(type.getOpcode(IF_ICMPEQ), l6);
+
+ Label l7 = new Label();
+ mv.visitLabel(l7);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 5);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalName + ")V");
+ mv.visitLabel(l6);
+
+ mv.visitVarInsn(type.getOpcode(ILOAD), 5);
+ mv.visitInsn(type.getOpcode(IRETURN));
+ break;
+
+ case Type.LONG:
+ internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+ unboxingMethod = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][2];
+
+ Label l00 = new Label();
+ mv.visitLabel(l00);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l10 = new Label();
+ mv.visitJumpInsn(IFNE, l10);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitInsn(LRETURN);
+ mv.visitLabel(l10);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitVarInsn(LSTORE, 1);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(LLOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 3); // Double Space
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 4);
+
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitTypeInsn(CHECKCAST, boxingType);
+ mv.visitVarInsn(ASTORE, 5);
+
+ mv.visitVarInsn(ALOAD, 5);
+ mv.visitMethodInsn(INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + internalName);
+ mv.visitVarInsn(LSTORE, 6);
+
+ l5 = new Label();
+ mv.visitLabel(l5);
+
+ mv.visitVarInsn(LLOAD, 1);
+ mv.visitVarInsn(LLOAD, 6);
+ mv.visitInsn(LCMP);
+ l6 = new Label();
+ mv.visitJumpInsn(IFEQ, l6);
+
+ l7 = new Label();
+ mv.visitLabel(l7);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(LLOAD, 6);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalName + ")V");
+ mv.visitLabel(l6);
+
+ mv.visitVarInsn(LLOAD, 6);
+ mv.visitInsn(LRETURN);
+
+ break;
+
+ case Type.DOUBLE:
+ internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+ unboxingMethod = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][2];
+
+ Label l01 = new Label();
+ mv.visitLabel(l01);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l11 = new Label();
+ mv.visitJumpInsn(IFNE, l11);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitInsn(DRETURN);
+ mv.visitLabel(l11);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitVarInsn(DSTORE, 1);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(DLOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 3); // Double Space
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 4);
+
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitTypeInsn(CHECKCAST, boxingType);
+ mv.visitVarInsn(ASTORE, 5);
+
+ mv.visitVarInsn(ALOAD, 5);
+ mv.visitMethodInsn(INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + internalName);
+ mv.visitVarInsn(DSTORE, 6);
+
+ l5 = new Label();
+ mv.visitLabel(l5);
+
+ mv.visitVarInsn(DLOAD, 1);
+ mv.visitVarInsn(DLOAD, 6);
+ mv.visitInsn(DCMPL);
+ l6 = new Label();
+ mv.visitJumpInsn(IFEQ, l6);
+
+ l7 = new Label();
+ mv.visitLabel(l7);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(DLOAD, 6);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalName + ")V");
+ mv.visitLabel(l6);
+
+ mv.visitVarInsn(DLOAD, 6);
+ mv.visitInsn(DRETURN);
+
+ break;
+
+ case Type.FLOAT:
+ internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+ unboxingMethod = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][2];
+
+ Label l02 = new Label();
+ mv.visitLabel(l02);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l12 = new Label();
+ mv.visitJumpInsn(IFNE, l12);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitInsn(FRETURN);
+ mv.visitLabel(l12);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
+ mv.visitVarInsn(FSTORE, 1);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(FLOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 2); // One Space
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 3);
+
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitTypeInsn(CHECKCAST, boxingType);
+ mv.visitVarInsn(ASTORE, 4);
+
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitMethodInsn(INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + internalName);
+ mv.visitVarInsn(FSTORE, 5);
+
+ l5 = new Label();
+ mv.visitLabel(l5);
+
+ mv.visitVarInsn(FLOAD, 1);
+ mv.visitVarInsn(FLOAD, 5);
+ mv.visitInsn(FCMPL);
+ l6 = new Label();
+ mv.visitJumpInsn(IFEQ, l6);
+
+ l7 = new Label();
+ mv.visitLabel(l7);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(FLOAD, 5);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalName + ")V");
+ mv.visitLabel(l6);
+
+ mv.visitVarInsn(FLOAD, 5);
+ mv.visitInsn(FRETURN);
+
+ break;
+
+ case Type.OBJECT:
+
+ Label l03 = new Label();
+ mv.visitLabel(l03);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l13 = new Label();
+ mv.visitJumpInsn(IFNE, l13);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, "L" + type.getInternalName() + ";");
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l13);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, name, "L" + type.getInternalName() + ";");
+ mv.visitVarInsn(ASTORE, 1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitVarInsn(ASTORE, 2);
+
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, type.getInternalName());
+ mv.visitVarInsn(ASTORE, 3);
+
+ Label l3b = new Label();
+ mv.visitLabel(l3b);
+
+ mv.visitVarInsn(ALOAD, 1);
+ Label l4b = new Label();
+ mv.visitJumpInsn(IFNULL, l4b);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
+
+ Label l5b = new Label();
+ mv.visitJumpInsn(IFNE, l5b);
+ mv.visitLabel(l4b);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(L" + type.getInternalName() + ";)V");
+ mv.visitLabel(l5b);
+
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ARETURN);
+
+ break;
+
+ default:
+ ManipulationProperty.getLogger().log(Level.SEVERE, "Manipulation problem in " + m_owner + " : a type is not implemented : " + type);
+ break;
+ }
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Create the setter method for one property. The name of the method is _set+name of the field
+ *
+ * @param name :
+ * name of the field representing a property
+ * @param desc :
+ * description of the setter method
+ * @param type :
+ * type of the property
+ */
+ private void createSimpleSetter(String name, String desc, Type type) {
+ MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "_set" + name, desc, null, null);
+
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ case Type.FLOAT:
+ String internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ String boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, name, internalName);
+ Label l1 = new Label();
+ mv.visitLabel(l1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l22 = new Label();
+ mv.visitJumpInsn(IFNE, l22);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(l22);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 2);
+
+ Label l2 = new Label();
+ mv.visitLabel(l2);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ Label l3 = new Label();
+ mv.visitLabel(l3);
+ mv.visitInsn(RETURN);
+ break;
+
+ case Type.LONG:
+ case Type.DOUBLE:
+ internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
+ boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, name, internalName);
+ l1 = new Label();
+ mv.visitLabel(l1);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l23 = new Label();
+ mv.visitJumpInsn(IFNE, l23);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(l23);
+
+ mv.visitTypeInsn(NEW, boxingType);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(type.getOpcode(ILOAD), 1);
+ mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
+ mv.visitVarInsn(ASTORE, 3); // Double space
+
+ l2 = new Label();
+ mv.visitLabel(l2);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ l3 = new Label();
+ mv.visitLabel(l3);
+ mv.visitInsn(RETURN);
+ break;
+
+ case Type.OBJECT:
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, name, "L" + type.getInternalName() + ";");
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_F" + name, "Z");
+ Label l24 = new Label();
+ mv.visitJumpInsn(IFNE, l24);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(l24);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ mv.visitInsn(RETURN);
+ break;
+ default:
+ ManipulationProperty.getLogger().log(Level.SEVERE, "Manipulation Error : Cannot create the setter method for the field : " + name + " (" + type + ")");
+ break;
+ }
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java
new file mode 100644
index 0000000..5af9933
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java
@@ -0,0 +1,84 @@
+/*
+ * 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.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Store properties for the manipulation process.
+ *
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ *
+ */
+public class ManipulationProperty {
+
+ /**
+ * Manipulator logger.
+ */
+ private static Logger m_logger;
+
+ /**
+ * Default logger level.
+ */
+ private static Level m_logLevel = Level.WARNING;
+
+ /**
+ * Get the manipulator logger.
+ * @return the logger used by the manipulator.
+ */
+ public static Logger getLogger() {
+ if (m_logger == null) {
+ String name = "org.apache.felix.ipojo.manipulator";
+ m_logger = Logger.getLogger(name);
+ m_logger.setLevel(m_logLevel);
+ }
+ return m_logger;
+ }
+
+ /**
+ * iPOJO Package name.
+ */
+ protected static final String IPOJO_PACKAGE_NAME = "org.apache.felix.ipojo";
+
+ /**
+ * Activator internal package name.
+ */
+ protected static final String IPOJO_INTERNAL_PACKAGE_NAME = "org/apache/felix/ipojo/";
+
+ /**
+ * Ipojo internal package name for internal descriptor.
+ */
+ protected static final String IPOJO_INTERNAL_DESCRIPTOR = "L" + IPOJO_INTERNAL_PACKAGE_NAME;
+
+ /**
+ * Helper array for bytecode manipulation of primitive type.
+ */
+ protected static final String[][] PRIMITIVE_BOXING_INFORMATION = new String[][] { { "V", "ILLEGAL", "ILLEGAL" }, // Void type [0]
+ { "Z", "java/lang/Boolean", "booleanValue" }, // boolean [1]
+ { "C", "java/lang/Character", "charValue" }, // char [2]
+ { "B", "java/lang/Byte", "byteValue" }, // byte [3]
+ { "S", "java/lang/Short", "shortValue" }, // short [4]
+ { "I", "java/lang/Integer", "intValue" }, // int [5]
+ { "F", "java/lang/Float", "floatValue" }, // float [6]
+ { "J", "java/lang/Long", "longValue" }, // long [7]
+ { "D", "java/lang/Double", "doubleValue" } // double [8]
+ };
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
new file mode 100644
index 0000000..4063343
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
@@ -0,0 +1,241 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * iPOJO Bytecode Manipulator.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ *
+ */
+public class Manipulator {
+ /**
+ * Store the visited fields : [name fo the field, type of the field].
+ */
+ private HashMap m_fields = new HashMap();
+
+ /**
+ * Store the interface implemented by the class.
+ */
+ private String[] m_interfaces = new String[0];
+
+ /**
+ * Store the methods list.
+ */
+ private List m_methods = new ArrayList();
+
+ /**
+ * Manipulate the class.
+ *
+ * @param name : The name of the class
+ * @param outputDirectory : output directory where the class if stored.
+ * @return true if the class is correctly manipulated.
+ * @throws Exception : throwed if the manipulation failed.
+ */
+ public boolean manipulate(String name, File outputDirectory) throws Exception {
+
+ // Init field, itfs and methods
+ m_fields = new HashMap();
+ m_interfaces = new String[0];
+ m_methods = new ArrayList();
+
+ // gets an input stream to read the bytecode of the class
+ String path = outputDirectory + "/" + name.replace('.', '/') + ".class";
+ File clazz = new File(path);
+
+ if (!clazz.exists()) {
+ return false;
+ }
+
+ URL url = clazz.toURI().toURL();
+
+ // if (url == null) { throw new ClassNotFoundException(name); }
+ ManipulationProperty.getLogger().log(Level.INFO, "Manipulate the class file : " + clazz.getAbsolutePath());
+
+ InputStream is1 = url.openStream();
+
+ // First check if the class is already manipulated :
+ ClassReader ckReader = new ClassReader(is1);
+ ClassChecker ck = new ClassChecker();
+ ckReader.accept(ck, ClassReader.SKIP_FRAMES);
+ is1.close();
+
+ m_fields = ck.getFields();
+
+ // Get interface and remove POJO interface is presents
+ String[] its = ck.getInterfaces();
+ ArrayList l = new ArrayList();
+ for (int i = 0; i < its.length; i++) {
+ l.add(its[i]);
+ }
+ l.remove("org/apache/felix/ipojo/Pojo");
+
+ m_interfaces = new String[l.size()];
+ for (int i = 0; i < m_interfaces.length; i++) {
+ m_interfaces[i] = ((String) l.get(i)).replace('/', '.');
+ }
+
+ // Get the method list
+ // Remove iPOJO methods
+ for (int i = 0; i < ck.getMethods().size(); i++) {
+ MethodDescriptor method = (MethodDescriptor) ck.getMethods().get(i);
+ if (!(method.getName().startsWith("_get") || // Avoid getter method
+ method.getName().startsWith("_set") || // Avoid setter method
+ method.getName().equals("_setComponentManager") || // Avoid the set method
+ method.getName().equals("getComponentInstance"))) { // Avoid the getComponentInstance method
+ System.err.println(" Add the method : " + method);
+ m_methods.add(method);
+ }
+ }
+
+ if (!ck.isalreadyManipulated()) {
+
+ // Manipulation ->
+ // Add the _setComponentManager method
+ // Instrument all fields
+ InputStream is2 = url.openStream();
+ ClassReader cr0 = new ClassReader(is2);
+ ClassWriter cw0 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ PojoAdapter preprocess = new PojoAdapter(cw0);
+ cr0.accept(preprocess, ClassReader.SKIP_FRAMES);
+ is2.close();
+
+ try {
+ FileOutputStream fos = new FileOutputStream(clazz);
+
+ fos.write(cw0.toByteArray());
+
+ fos.close();
+ ManipulationProperty.getLogger().log(Level.INFO, "Put the file " + clazz.getAbsolutePath() + " in the jar file");
+ } catch (Exception e) {
+ System.err.println("Problem to write the adapted class on the file system " + " [ " + clazz.getAbsolutePath() + " ] " + e.getMessage());
+ }
+ }
+ // The file is in the bundle
+ return true;
+ }
+
+ /**
+ * Manipulate the given byte array.
+ *
+ * @param origin :
+ * original class.
+ * @return the manipulated class.
+ * @throws IOException :
+ * if an error occurs during the manipulation.
+ */
+ public byte[] manipulate(byte[] origin) throws IOException {
+ InputStream is1 = new ByteArrayInputStream(origin);
+
+ // First check if the class is already manipulated :
+ ClassReader ckReader = new ClassReader(is1);
+ ClassChecker ck = new ClassChecker();
+ ckReader.accept(ck, ClassReader.SKIP_FRAMES);
+ is1.close();
+
+ m_fields = ck.getFields();
+
+ // Get interface and remove POJO interface is presents
+ String[] its = ck.getInterfaces();
+ ArrayList l = new ArrayList();
+ for (int i = 0; i < its.length; i++) {
+ l.add(its[i]);
+ }
+ l.remove("org/apache/felix/ipojo/Pojo");
+
+ m_interfaces = new String[l.size()];
+ for (int i = 0; i < m_interfaces.length; i++) {
+ m_interfaces[i] = ((String) l.get(i)).replace('/', '.');
+ }
+
+ for (int i = 0; i < ck.getMethods().size(); i++) {
+ MethodDescriptor method = (MethodDescriptor) ck.getMethods().get(i);
+ if (!(method.getName().startsWith("_get") || // Avoid getter method
+ method.getName().startsWith("_set") || // Avoid setter method
+ method.getName().equals("_setComponentManager") || // Avoid the set method
+ method.getName().equals("getComponentInstance"))) { // Avoid the getComponentInstance method
+ m_methods.add(method);
+ }
+ }
+
+ ClassWriter finalWriter = null;
+ if (!ck.isalreadyManipulated()) {
+ // Manipulation ->
+ // Add the _setComponentManager method
+ // Instrument all fields
+ InputStream is2 = new ByteArrayInputStream(origin);
+ ClassReader cr0 = new ClassReader(is2);
+ ClassWriter cw0 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ PojoAdapter preprocess = new PojoAdapter(cw0);
+ cr0.accept(preprocess, ClassReader.SKIP_FRAMES);
+ is2.close();
+ finalWriter = cw0;
+ }
+ // The file is in the bundle
+ return finalWriter.toByteArray();
+ }
+
+ /**
+ * Compute component type manipulation metadata.
+ * @return the manipulation metadata of the class.
+ */
+ public Element getManipulationMetadata() {
+ Element elem = new Element("Manipulation", "");
+ for (int j = 0; j < m_interfaces.length; j++) {
+ Element itf = new Element("Interface", "");
+ Attribute att = new Attribute("name", m_interfaces[j]);
+ itf.addAttribute(att);
+ elem.addElement(itf);
+ }
+ for (Iterator it = m_fields.keySet().iterator(); it.hasNext();) {
+ Element field = new Element("Field", "");
+ String name = (String) it.next();
+ String type = (String) m_fields.get(name);
+ Attribute attName = new Attribute("name", name);
+ Attribute attType = new Attribute("type", type);
+ field.addAttribute(attName);
+ field.addAttribute(attType);
+ elem.addElement(field);
+ }
+
+ for (int j = 0; j < m_methods.size(); j++) {
+ MethodDescriptor method = (MethodDescriptor) m_methods.get(j);
+ elem.addElement(method.getElement());
+ }
+
+ return elem;
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java
new file mode 100644
index 0000000..d240be6
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java
@@ -0,0 +1,174 @@
+/*
+ * 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.logging.Level;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.AdviceAdapter;
+
+/**
+ * Insert code calling callbacks at the entry and before the exit of a method.
+ * Moreover it replaces all GETFIELD and SETFIELD by getter and setter invocation.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class MethodCodeAdapter extends AdviceAdapter implements Opcodes {
+
+ /**
+ * The owner class of the field. m_owner : String
+ */
+ private String m_owner;
+
+ /**
+ * Name of the method.
+ */
+ private String m_name;
+
+ /**
+ * MethodCodeAdapter constructor.
+ * @param mv : MethodVisitor
+ * @param owner : Name of the class
+ * @param access : Method acess
+ * @param name : Method name
+ * @param desc : Method descriptor
+ */
+ public MethodCodeAdapter(final MethodVisitor mv, final String owner, int access, String name, String desc) {
+ super(mv, access, name, desc);
+ m_owner = owner;
+ m_name = name;
+ }
+
+ /**
+ * Visit an instruction modifying a method (GETFIELD/PUTFIELD).
+ * @see org.objectweb.asm.MethodVisitor#visitFieldInsn(int, String, String, String)
+ * @param opcode : visited operation code
+ * @param owner : owner of the field
+ * @param name : name of the field
+ * @param desc : decriptor of the field
+ */
+ public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
+ if (owner.equals(m_owner)) {
+ if (opcode == GETFIELD) {
+ ManipulationProperty.getLogger().log(Level.INFO, "Manipulate a GETFIELD on : " + name);
+ String gDesc = "()" + desc;
+ visitMethodInsn(INVOKEVIRTUAL, owner, "_get" + name, gDesc);
+ return;
+ } else if (opcode == PUTFIELD) {
+ ManipulationProperty.getLogger().log(Level.INFO, "Manipulate a PUTFIELD on : " + name);
+ String sDesc = "(" + desc + ")V";
+ visitMethodInsn(INVOKESPECIAL, owner, "_set" + name, sDesc);
+ return;
+ }
+ }
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ /**
+ * Method injecting call at the entry of each method.
+ * @see org.objectweb.asm.commons.AdviceAdapter#onMethodEnter()
+ */
+ protected void onMethodEnter() {
+ Type[] args = Type.getArgumentTypes(methodDesc);
+ String name = m_name;
+
+ for (int i = 0; i < args.length; i++) {
+ String cn = args[i].getClassName();
+ if (cn.endsWith("[]")) {
+ cn = cn.replace("[]", "$");
+ }
+ cn = cn.replace(".", "_");
+ name += cn;
+ }
+
+ String flag = "_M" + name;
+
+ Label l0 = new Label();
+ mv.visitLabel(l0);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, flag, "Z");
+ Label l1 = new Label();
+ mv.visitJumpInsn(IFEQ, l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "entryCallback", "(Ljava/lang/String;)V");
+ mv.visitLabel(l1);
+ }
+
+ /**
+ * Method injecting call at the exit of each method.
+ * @param opcode : returned opcode (ARETURN, IRETURN, ATHROW ...)
+ * @see org.objectweb.asm.commons.AdviceAdapter#onMethodExit(int)
+ */
+ protected void onMethodExit(int opcode) {
+ Type[] args = Type.getArgumentTypes(methodDesc);
+ String name = m_name;
+
+ for (int i = 0; i < args.length; i++) {
+ String cn = args[i].getClassName();
+ if (cn.endsWith("[]")) {
+ cn = cn.replace("[]", "$");
+ }
+ cn = cn.replace(".", "_");
+ name += cn;
+ }
+
+ String flag = "_M" + name;
+
+ int local = newLocal(Type.getType(Object.class));
+ if (opcode == RETURN) {
+ visitInsn(ACONST_NULL);
+ } else if (opcode != ARETURN && opcode != ATHROW) {
+ box(Type.getReturnType(this.methodDesc));
+ }
+
+ mv.visitVarInsn(ASTORE, local);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, flag, "Z");
+ Label l5 = new Label();
+ mv.visitJumpInsn(IFEQ, l5);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitLdcInsn(name);
+ mv.visitVarInsn(ALOAD, local);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "exitCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ mv.visitLabel(l5);
+ if (opcode == ARETURN || opcode == ATHROW) {
+ mv.visitVarInsn(ALOAD, local);
+ } else if (opcode != RETURN) {
+ mv.visitVarInsn(ALOAD, local);
+ unbox(Type.getReturnType(this.methodDesc));
+ }
+ }
+
+ /**
+ * Compute mx local and max stack size.
+ * @param maxStack : new stack size.
+ * @param maxLocals : max local (do not modified, super will update it automatically).
+ * @see org.objectweb.asm.commons.LocalVariablesSorter#visitMaxs(int, int)
+ */
+ public void visitMaxs(int maxStack, int maxLocals) {
+ super.visitMaxs(maxStack + 1, maxLocals);
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java
new file mode 100644
index 0000000..184b2f2
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java
@@ -0,0 +1,129 @@
+/*
+ * 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 org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.objectweb.asm.Type;
+
+/**
+ * Method Descriptor describe a method.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class MethodDescriptor {
+
+ /**
+ * Method name.
+ */
+ private String m_name;
+
+ /**
+ * Returned type.
+ */
+ private String m_returnType;
+
+ /**
+ * Argument types.
+ */
+ private String[] m_arguments;
+
+ /**
+ * Constructor.
+ * @param name : name of the method.
+ * @param desc : descriptor of the method.
+ */
+ public MethodDescriptor(String name, String desc) {
+ m_name = name;
+ Type ret = Type.getReturnType(desc);
+ Type[] args = Type.getArgumentTypes(desc);
+
+ m_returnType = getType(ret);
+ m_arguments = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ m_arguments[i] = getType(args[i]);
+ }
+ }
+
+ /**
+ * Compute method manipulation metadata.
+ * @return the element containing metadata about this method.
+ */
+ public Element getElement() {
+ Element method = new Element("method", "");
+ method.addAttribute(new Attribute("name", m_name));
+
+ // Add return
+ if (!m_returnType.equals("void")) {
+ method.addAttribute(new Attribute("return", m_returnType));
+ }
+
+ // Add arguments
+ if (m_arguments.length > 0) {
+ String args = "{";
+ args += m_arguments[0];
+ for (int i = 1; i < m_arguments.length; i++) {
+ args += "," + m_arguments[i];
+ }
+ args += "}";
+ method.addAttribute(new Attribute("arguments", args));
+ }
+
+ return method;
+ }
+
+ /**
+ * Get the iPOJO internal type for the given type.
+ * @param type : type.
+ * @return the iPOJO internal type.
+ */
+ private String getType(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ Type elemType = type.getElementType();
+ return getType(elemType) + "[]";
+ case Type.BOOLEAN:
+ return "boolean";
+ case Type.BYTE:
+ return "byte";
+ case Type.CHAR:
+ return "char";
+ case Type.DOUBLE:
+ return "double";
+ case Type.FLOAT:
+ return "float";
+ case Type.INT:
+ return "int";
+ case Type.LONG:
+ return "long";
+ case Type.OBJECT:
+ return type.getClassName();
+ case Type.SHORT:
+ return "short";
+ case Type.VOID:
+ return "void";
+ default:
+ return "unknown";
+ }
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java
new file mode 100644
index 0000000..ad427cd
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java
@@ -0,0 +1,362 @@
+/*
+ * 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.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Manipulate a POJO class.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class PojoAdapter extends ClassAdapter implements Opcodes {
+
+ /**
+ * POJO interface.
+ */
+ private final String m_pojo = "org/apache/felix/ipojo/Pojo";
+
+ /**
+ * The owner. m_owner : String
+ */
+ private String m_owner;
+
+ /**
+ * Field list.
+ */
+ private List m_fields = new ArrayList();
+
+ /**
+ * Method list.
+ */
+ private List m_methods = new ArrayList();
+
+ /**
+ * Getter/Setter methods creator.
+ */
+ FieldAdapter m_getterSetterCreator = new FieldAdapter(cv);
+
+ /**
+ * Constructor.
+ * @param arg0 : class adapter on which delegate.
+ */
+ public PojoAdapter(ClassVisitor arg0) {
+ super(arg0);
+ }
+
+ /**
+ * Start visiting a class.
+ * Initialize the getter/setter generator, add the _cm field, add the pojo interface.
+ * @param version : class version
+ * @param access : class access
+ * @param name : class name
+ * @param signature : class signature
+ * @param superName : class super class
+ * @param interfaces : implemented interfaces
+ * @see org.objectweb.asm.ClassAdapter#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_owner = name;
+ m_getterSetterCreator.visit(version, access, name, signature, superName, interfaces);
+ addCMField();
+ addPOJOInterface(version, access, name, signature, superName, interfaces);
+ }
+
+ /**
+ * Visit an annotation.
+ * @param desc : annotation descriptor.
+ * @param visible : is the annotation visible at runtime.
+ * @return the annotation visitor.
+ * @see org.objectweb.asm.ClassAdapter#visitAnnotation(java.lang.String, boolean)
+ */
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return super.visitAnnotation(desc, visible);
+ }
+
+ /**
+ * Visit an attribute.
+ * @param attr : visited attribute
+ * @see org.objectweb.asm.ClassAdapter#visitAttribute(org.objectweb.asm.Attribute)
+ */
+ public void visitAttribute(Attribute attr) {
+ super.visitAttribute(attr);
+ }
+
+ /**
+ * Visit end.
+ * Create helper methods.
+ * @see org.objectweb.asm.ClassAdapter#visitEnd()
+ */
+ public void visitEnd() {
+ // Create the _cmSetter(ComponentManager cm) method
+ createComponentManagerSetter();
+
+ // Add the getComponentInstance
+ createGetComponentInstanceMethod();
+
+ m_methods.clear();
+ m_fields.clear();
+
+ cv.visitEnd();
+ }
+
+ /**
+ * Visit a field.
+ * Call the adapter generating getter and setter methods.
+ * @param access : field acess.
+ * @param name : field name
+ * @param desc : field descriptor
+ * @param signature : field signature
+ * @param value : field value (static field only)
+ * @return the field visitor.
+ * @see org.objectweb.asm.ClassAdapter#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) {
+ // Add the field to the list.
+ if ((access & ACC_STATIC) == 0) {
+ m_fields.add(name);
+ addFlagField(name);
+ m_getterSetterCreator.visitField(access, name, desc, signature, value);
+ }
+
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ /**
+ * Visit an inner class.
+ * @param name : class name
+ * @param outerName : outer name
+ * @param innerName : inner name
+ * @param access : class access
+ * @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
+ */
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ super.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ /**
+ * Visit a method.
+ * Manipulate constructor and methods. Does nothing with clinit and class$
+ * @param access : method acess
+ * @param name : method name
+ * @param desc : method descriptor
+ * @param signature : method signature
+ * @param exceptions : method exceptions
+ * @return the Method Visitor.
+ * @see org.objectweb.asm.ClassAdapter#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>") || name.equals("class$")) {
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+ if (name.equals("<init>")) {
+ // 1) change the constructor descriptor (add a component manager arg as first argument)
+ String newDesc = desc.substring(1);
+ newDesc = "(Lorg/apache/felix/ipojo/InstanceManager;" + newDesc;
+
+ // Insert the new constructor
+ MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "<init>", newDesc, null, null);
+
+ if (mv == null) {
+ return null;
+ } else {
+ return new ConstructorCodeAdapter(mv, access, desc, m_owner);
+ }
+ } else {
+ Type[] args = Type.getArgumentTypes(desc);
+ String id = name;
+ for (int i = 0; i < args.length; i++) {
+ String cn = args[i].getClassName();
+ if (cn.endsWith("[]")) {
+ cn = cn.replace("[]", "$");
+ }
+ cn = cn.replace(".", "_");
+ id += cn;
+ }
+
+ String flag = "_M" + id;
+ m_methods.add(id);
+
+ FieldVisitor flagField = cv.visitField(Opcodes.ACC_PRIVATE, flag, "Z", null, null);
+ if (flagField != null) {
+ flagField.visitEnd();
+ }
+
+ MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+ return new MethodCodeAdapter(mv, m_owner, access, name, desc);
+ }
+ }
+
+ /**
+ * Visit an outer class.
+ * @param owner : class owner
+ * @param name : class name
+ * @param desc : class descriptor.
+ * @see org.objectweb.asm.ClassAdapter#visitOuterClass(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void visitOuterClass(String owner, String name, String desc) {
+ super.visitOuterClass(owner, name, desc);
+ }
+
+ /**
+ * Visit source.
+ * @param source : source
+ * @param debug : debug
+ * @see org.objectweb.asm.ClassAdapter#visitSource(java.lang.String, java.lang.String)
+ */
+ public void visitSource(String source, String debug) {
+ super.visitSource(source, debug);
+ }
+
+ /**
+ * Add the instance manager field (_cm).
+ */
+ private void addCMField() {
+ // Insert _cm field
+ FieldVisitor fv = super.visitField(ACC_PRIVATE, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;", null, null);
+ fv.visitEnd();
+ }
+
+ /**
+ * Add the POJO interface to the visited class.
+ * @param version : class version
+ * @param access : class access
+ * @param name : class name
+ * @param signature : class signature
+ * @param superName : super class
+ * @param interfaces : implemented interfaces.
+ */
+ private void addPOJOInterface(int version, int access, String name, String signature, String superName, String[] interfaces) {
+
+ // Add the POJO interface to the interface list
+ // Check that the POJO interface isnot already in the list
+ boolean found = false;
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i].equals(m_pojo)) {
+ found = true;
+ }
+ }
+ String[] itfs;
+ if (!found) {
+ itfs = new String[interfaces.length + 1];
+ for (int i = 0; i < interfaces.length; i++) {
+ itfs[i] = interfaces[i];
+ }
+ itfs[interfaces.length] = m_pojo;
+ } else {
+ itfs = interfaces;
+ }
+
+ String str = "";
+ for (int i = 0; i < itfs.length; i++) {
+ str += itfs[i] + " ";
+ }
+
+ cv.visit(version, access, name, signature, superName, itfs);
+ }
+
+ /**
+ * Create the setter method for the _cm field.
+ */
+ private void createComponentManagerSetter() {
+ MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "_setComponentManager", "(Lorg/apache/felix/ipojo/InstanceManager;)V", null, null);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getRegistredFields", "()Ljava/util/Set;");
+ mv.visitVarInsn(ASTORE, 2);
+
+ for (int i = 0; i < m_fields.size(); i++) {
+ String field = (String) m_fields.get(i);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitLdcInsn(field);
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "contains", "(Ljava/lang/Object;)Z");
+ Label l3 = new Label();
+ mv.visitJumpInsn(IFEQ, l3);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ICONST_1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, "_F" + field, "Z");
+ mv.visitLabel(l3);
+ }
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getRegistredMethods", "()Ljava/util/Set;");
+ mv.visitVarInsn(ASTORE, 2);
+
+ for (int i = 0; i < m_methods.size(); i++) {
+ String methodId = (String) m_methods.get(i);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitLdcInsn(methodId);
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "contains", "(Ljava/lang/Object;)Z");
+ Label l3 = new Label();
+ mv.visitJumpInsn(IFEQ, l3);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ICONST_1);
+ mv.visitFieldInsn(PUTFIELD, m_owner, "_M" + methodId, "Z");
+ mv.visitLabel(l3);
+ }
+
+ mv.visitInsn(RETURN);
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Create the getComponentInstance method.
+ */
+ private void createGetComponentInstanceMethod() {
+ MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "getComponentInstance", "()Lorg/apache/felix/ipojo/ComponentInstance;", null, null);
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
+ mv.visitInsn(ARETURN);
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Add the flag field.
+ * @param name : name of the field
+ */
+ private void addFlagField(String name) {
+ // Add the flag field
+ FieldVisitor flag = cv.visitField(Opcodes.ACC_PRIVATE, "_F" + name, "Z", null, null);
+ if (flag != null) {
+ flag.visitEnd();
+ }
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
new file mode 100644
index 0000000..0b34c5b
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
@@ -0,0 +1,610 @@
+/*
+ * 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.manipulator;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.apache.felix.ipojo.manipulation.Manipulator;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.xml.parser.ParseException;
+import org.apache.felix.ipojo.xml.parser.XMLMetadataParser;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Pojoization allows creating an iPOJO bundle from a "normal" bundle.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class Pojoization {
+
+ /**
+ * List of component types.
+ */
+ private List m_components;
+
+ /**
+ * Metadata (in internal format).
+ */
+ private Element[] m_metadata;
+
+ /**
+ * Errors occured during the manipulation.
+ */
+ private List m_errors = new ArrayList();
+
+ /**
+ * Warnings occured during the manipulation.
+ */
+ private List m_warnings = new ArrayList();
+
+ /**
+ * Class map (jar entry, byte[]).
+ */
+ private HashMap m_classes = new HashMap();
+
+ /**
+ * Referendec packages by the composite.
+ */
+ private List m_referredPackages;
+
+ /**
+ * Add an error in the error list.
+ * @param mes : error message.
+ */
+ private void error(String mes) {
+ System.out.println("An error occurs during the pojoization : " + mes);
+ m_errors.add(mes);
+ }
+
+ /**
+ * Add a warning in the warning list.
+ * @param mes : warning message
+ */
+ public void warn(String mes) {
+ System.out.println("An warning occurs during the pojoization : " + mes);
+ m_warnings.add(mes);
+ }
+
+ public List getErrors() {
+ return m_errors;
+ }
+
+ /**
+ * Manipulate a normal bundle.
+ * It will create an iPOJO bundle based on the given metadata file.
+ * The original and final bundle must be different.
+ * @param in : original bundle.
+ * @param out : final bundle.
+ * @param metadataFile : iPOJO metadata file (XML).
+ */
+ public void pojoization(File in, File out, File metadataFile) {
+ // Get the metadata.xml location
+ String path = metadataFile.getAbsolutePath();
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ m_metadata = parseXMLMetadata(path);
+
+ // Get the list of declared component
+ m_components = getDeclaredComponents(m_metadata);
+
+ // Start the manipulation
+ manipulation(in, out);
+
+ // Check that all declared components are manipulated
+ for (int i = 0; i < m_components.size(); i++) {
+ ComponentInfo ci = (ComponentInfo) m_components.get(i);
+ if (!ci.m_isManipulated) {
+ error("The component " + ci.m_classname + " is declared but not in the bundle");
+ }
+ }
+ }
+
+ /**
+ * Manipulate the Bundle.
+ * @param in : original bundle
+ * @param out : final bundle
+ */
+ private void manipulation(File in, File out) {
+ // Get a jar file from the given file
+ JarFile inputJar = null;
+ try {
+ inputJar = new JarFile(in);
+ } catch (IOException e) {
+ error("Cannot the input file is not a JarFile : " + in.getAbsolutePath());
+ return;
+ }
+
+ manipulateComponents(inputJar); // Manipulate classes
+ Manifest mf = doManifest(inputJar); // Compute the manifest
+
+ // Create a new Jar file
+ FileOutputStream fos = null;
+ JarOutputStream jos = null;
+ try {
+ fos = new FileOutputStream(out);
+ jos = new JarOutputStream(fos, mf);
+ } catch (FileNotFoundException e1) {
+ error("Cannot manipulate the Jar file : the file " + out.getAbsolutePath() + " not found");
+ return;
+ } catch (IOException e) {
+ error("Cannot manipulate the Jar file : cannot access to " + out.getAbsolutePath());
+ return;
+ }
+
+ try {
+ // Copy classes and resources
+ Enumeration entries = inputJar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry curEntry = (JarEntry) entries.nextElement();
+ // Check if we need to manipulate the clazz
+ if (m_classes.containsKey(curEntry.getName())) {
+ JarEntry je = new JarEntry(curEntry.getName());
+ byte[] outClazz = (byte[]) m_classes.get(curEntry.getName());
+ if (outClazz.length != 0) {
+ jos.putNextEntry(je); // copy the entry header to jos
+ jos.write(outClazz);
+ jos.closeEntry();
+ } else { // The class is already manipulated
+ InputStream currIn = inputJar.getInputStream(curEntry);
+ int c;
+ int i = 0;
+ while ((c = currIn.read()) >= 0) {
+ jos.write(c);
+ i++;
+ }
+ currIn.close();
+ jos.closeEntry();
+ }
+ } else {
+ // Do not copy the manifest
+ if (!curEntry.getName().equals("META-INF/MANIFEST.MF")) {
+ // copy the entry header to jos
+ jos.putNextEntry(curEntry);
+ InputStream currIn = inputJar.getInputStream(curEntry);
+ int c;
+ int i = 0;
+ while ((c = currIn.read()) >= 0) {
+ jos.write(c);
+ i++;
+ }
+ currIn.close();
+ jos.closeEntry();
+ }
+ }
+ }
+ } catch (IOException e) {
+ error("Cannot manipulate the Jar file : " + e.getMessage() + " - Cause : " + e.getCause());
+ return;
+ }
+
+ try {
+ inputJar.close();
+ jos.close();
+ fos.close();
+ jos = null;
+ fos = null;
+ } catch (IOException e) {
+ error("Cannot close the new Jar file : " + e.getMessage() + " - Cause : " + e.getCause());
+ return;
+ }
+ }
+
+ /**
+ * Manipulate classes of the input Jar.
+ * @param inputJar : input bundle.
+ */
+ private void manipulateComponents(JarFile inputJar) {
+ Enumeration entries = inputJar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry curEntry = (JarEntry) entries.nextElement();
+ // Check if we need to manipulate the clazz
+ for (int i = 0; i < m_components.size(); i++) {
+ ComponentInfo ci = (ComponentInfo) m_components.get(i);
+ if (ci.m_classname.equals(curEntry.getName())) {
+ byte[] outClazz = manipulateComponent(inputJar, curEntry, ci);
+ m_classes.put(curEntry.getName(), outClazz);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create the manifest.
+ * Set the bundle activator, imports, iPOJO-components clauses
+ * @param initial : initial Jar file.
+ * @return the generated manifest.
+ */
+ private Manifest doManifest(JarFile initial) {
+ Manifest mf = null;
+ try {
+ mf = initial.getManifest(); // Get the initial manifest
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Attributes att = mf.getMainAttributes();
+ setBundleActivator(att); // Set the bundle activator
+ setImports(att); // Set the imports (add ipojo and handler namespaces
+ setPOJOMetadata(att); // Add iPOJO-Component
+ setCreatedBy(att); // Add iPOJO to the creators
+ return mf;
+ }
+
+ /**
+ * Manipulate a component class.
+ * @param inputJar : input bundle
+ * @param je : Jar entry of the classes
+ * @param ci : attached component info (containing metadata and manipulation metadata)
+ * @return the generated class (byte array)
+ */
+ private byte[] manipulateComponent(JarFile inputJar, JarEntry je, ComponentInfo ci) {
+ Manipulator man = new Manipulator();
+ try {
+ InputStream currIn = inputJar.getInputStream(je);
+ byte[] in = new byte[0];
+ int c;
+ while ((c = currIn.read()) >= 0) {
+ byte[] in2 = new byte[in.length + 1];
+ System.arraycopy(in, 0, in2, 0, in.length);
+ in2[in.length] = (byte) c;
+ in = in2;
+ }
+ currIn.close();
+ byte[] out = man.manipulate(in); // iPOJO manipulation
+ // Insert information to metadata
+ ci.m_componentMetadata.addElement(man.getManipulationMetadata());
+ ci.m_isManipulated = true;
+ return out;
+ } catch (IOException e) {
+ error("Cannot manipulate the class " + je.getName() + " : " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Return the list of "concrete" component.
+ * @param meta : metadata.
+ * @return the list of compionent info requiring a manipulation.
+ */
+ private List getDeclaredComponents(Element[] meta) {
+ List componentClazzes = new ArrayList();
+ for (int i = 0; i < meta.length; i++) {
+ if (meta[i].getName().equalsIgnoreCase("component") && meta[i].containsAttribute("className")) {
+ String name = meta[i].getAttribute("classname");
+ name = name.replace('.', '/');
+ name += ".class";
+ componentClazzes.add(new ComponentInfo(name, meta[i]));
+ }
+ }
+ return componentClazzes;
+ }
+
+ /**
+ * Component Info.
+ * Represent a component type to be manipulated or already manipulated.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+ private class ComponentInfo {
+ /**
+ * Component Type metadata.
+ */
+ Element m_componentMetadata;
+
+ /**
+ * Component Type implementation class.
+ */
+ String m_classname;
+
+ /**
+ * Is the class already manipulated.
+ */
+ boolean m_isManipulated;
+
+ /**
+ * Constructor.
+ * @param cn : class name
+ * @param met : component type metadata
+ */
+ ComponentInfo(String cn, Element met) {
+ this.m_classname = cn;
+ this.m_componentMetadata = met;
+ m_isManipulated = false;
+ }
+ }
+
+ /**
+ * Set the bundle activator in the manifest.
+ * @param att : manifest attribute.
+ */
+ private void setBundleActivator(Attributes att) {
+ if (att.containsKey("Bundle-Activator")) {
+ warn("The bundle contains another Bundle-Activator : " + att.getValue("Bundle-Activator") + " - Replace the existing one");
+ }
+ att.putValue("Bundle-Activator", "org.apache.felix.ipojo.Activator");
+ }
+
+ /**
+ * Set the create-by in the manifest.
+ * @param att : manifest attribute.
+ */
+ private void setCreatedBy(Attributes att) {
+ String prev = att.getValue("Created-By");
+ att.putValue("Created-By", prev + " & iPOJO");
+ }
+
+ /**
+ * Add imports to the given manifest attribute list. This method add ipojo imports and handler imports (if needed).
+ * @param att : the manifest attribute list to modify.
+ */
+ private void setImports(Attributes att) {
+ Map imports = parseHeader(att.getValue("Import-Package"));
+ Map ver = new TreeMap();
+ ver.put("version", "0.7.3");
+ if (!imports.containsKey("org.apache.felix.ipojo")) {
+ imports.put("org.apache.felix.ipojo", ver);
+ }
+ if (!imports.containsKey("org.apache.felix.ipojo.architecture")) {
+ imports.put("org.apache.felix.ipojo.architecture", ver);
+ }
+ if (!imports.containsKey("org.osgi.service.cm")) {
+ Map verCM = new TreeMap();
+ verCM.put("version", "1.2");
+ imports.put("org.osgi.service.cm", verCM);
+ }
+ if (!imports.containsKey("org.osgi.service.log")) {
+ Map verCM = new TreeMap();
+ verCM.put("version", "1.3");
+ imports.put("org.osgi.service.cm", verCM);
+ }
+
+
+ // Add handler namespace
+ String[][] namespaces = computeHandlerNamespace();
+ for (int j = 0; j < namespaces.length; j++) {
+ for (int k = 0; k < namespaces[j].length; k++) {
+ if (!namespaces[j][k].equals("")) {
+ int lastIndex = namespaces[j][k].lastIndexOf('.');
+ String ns = namespaces[j][k].substring(0, lastIndex);
+ if (!imports.containsKey(ns)) {
+ imports.put(ns, new TreeMap());
+ }
+ }
+ }
+ }
+
+ // Add refered imports from the metadata
+ for (int i = 0; i < m_referredPackages.size(); i++) {
+ String pack = (String) m_referredPackages.get(i);
+ imports.put(pack, new TreeMap());
+ }
+
+ // Write imports
+ att.putValue("Import-Package", printClauses(imports, "resolution:"));
+ }
+
+ /**
+ * Add iPOJO-Components to the given manifest attribute list. This method add the iPOJO-Components header and its value (according to the metadata) to the manifest.
+ * @param att : the manifest attribute list to modify.
+ */
+ private void setPOJOMetadata(Attributes att) {
+ String meta = "";
+ for (int i = 0; i < m_metadata.length; i++) {
+ meta += buildManifestMetadata(m_metadata[i], "");
+ }
+ att.putValue("iPOJO-Components", meta);
+ }
+
+ /**
+ * Build the list of namespaces used in the metadata. (first-order only).
+ * @return the list of namespaces [array of component [ array of namespace ] ].
+ */
+ private String[][] computeHandlerNamespace() {
+ String[][] ns = new String[m_metadata.length][];
+ for (int i = 0; i < m_metadata.length; i++) {
+ ns[i] = m_metadata[i].getNamespaces();
+ }
+ return ns;
+ }
+
+ /**
+ * Standard OSGi header parser. This parser can handle the format clauses ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '=' value )
+ * This is mapped to a Map { name => Map { attr|directive => value } }
+ *
+ * @param value : String to parse.
+ * @return parsed map.
+ */
+ public Map parseHeader(String value) {
+ if (value == null || value.trim().length() == 0) {
+ return new HashMap();
+ }
+
+ Map result = new LinkedHashMap();
+ QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+ char del;
+ do {
+ boolean hadAttribute = false;
+ Map clause = new HashMap();
+ List aliases = new ArrayList();
+ aliases.add(qt.nextToken());
+ del = qt.getSeparator();
+ while (del == ';') {
+ String adname = qt.nextToken();
+ if ((del = qt.getSeparator()) != '=') {
+ if (hadAttribute) {
+ throw new IllegalArgumentException("Header contains name field after attribute or directive: " + adname + " from " + value);
+ }
+ aliases.add(adname);
+ } else {
+ String advalue = qt.nextToken();
+ clause.put(adname, advalue);
+ del = qt.getSeparator();
+ hadAttribute = true;
+ }
+ }
+ for (Iterator i = aliases.iterator(); i.hasNext();) {
+ result.put(i.next(), clause);
+ }
+ } while (del == ',');
+ return result;
+ }
+
+ /**
+ * Print a standard Map based OSGi header.
+ *
+ * @param exports : map { name => Map { attribute|directive => value } }
+ * @param allowedDirectives : list of allowed directives.
+ * @return the clauses
+ */
+ public String printClauses(Map exports, String allowedDirectives) {
+ StringBuffer sb = new StringBuffer();
+ String del = "";
+ for (Iterator i = exports.keySet().iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ Map map = (Map) exports.get(name);
+ sb.append(del);
+ sb.append(name);
+
+ for (Iterator j = map.keySet().iterator(); j.hasNext();) {
+ String key = (String) j.next();
+
+ // Skip directives we do not recognize
+ if (key.endsWith(":") && allowedDirectives.indexOf(key) < 0) {
+ continue;
+ }
+
+ String value = (String) map.get(key);
+ sb.append(";");
+ sb.append(key);
+ sb.append("=");
+ boolean dirty = value.indexOf(',') >= 0 || value.indexOf(';') >= 0;
+ if (dirty) {
+ sb.append("\"");
+ }
+ sb.append(value);
+ if (dirty) {
+ sb.append("\"");
+ }
+ }
+ del = ", ";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parse XML Metadata.
+ * @param path : path of the file to parse.
+ * @return the parsed element array.
+ */
+ private Element[] parseXMLMetadata(String path) {
+ File metadata = new File(path);
+ URL url;
+ Element[] meta = null;
+ try {
+ url = metadata.toURI().toURL();
+ if (url == null) {
+ error("Cannot find the metdata file : " + path);
+ return null;
+ }
+
+ InputStream stream = url.openStream();
+ XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+ XMLMetadataParser handler = new XMLMetadataParser();
+ parser.setContentHandler(handler);
+ InputSource is = new InputSource(stream);
+ parser.parse(is);
+ meta = handler.getMetadata();
+ m_referredPackages = handler.getReferredPackages();
+ stream.close();
+
+ } catch (MalformedURLException e) {
+ error("Malformed Mtadata URL for " + path);
+ return null;
+ } catch (IOException e) {
+ error("Cannot open the file : " + path);
+ return null;
+ } catch (ParseException e) {
+ error("Parsing Error when parsing the XML file " + path + " : " + e.getMessage());
+ return null;
+ } catch (SAXException e) {
+ error("Parsing Error when parsing (Sax Error) the XML file " + path + " : " + e.getMessage());
+ return null;
+ }
+
+ if (meta == null || meta.length == 0) {
+ warn("Neither component, neither instance in " + path);
+ }
+
+ return meta;
+ }
+
+ /**
+ * Generate manipulation metadata.
+ * @param element : actual element.
+ * @param actual : actual manipulation metadata.
+ * @return : given amnipulation metadata + manipulation metadata of the given element.
+ */
+ private String buildManifestMetadata(Element element, String actual) {
+ String result = "";
+ if (element.getNameSpace().equals("")) {
+ result = actual + element.getName() + " { ";
+ } else {
+ result = actual + element.getNameSpace() + ":" + element.getName() + " { ";
+ }
+
+ for (int i = 0; i < element.getAttributes().length; i++) {
+ if (element.getAttributes()[i].getNameSpace().equals("")) {
+ result = result + "$" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+ } else {
+ result = result + "$" + element.getAttributes()[i].getNameSpace() + ":" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+ }
+ }
+
+ for (int i = 0; i < element.getElements().length; i++) {
+ result = buildManifestMetadata(element.getElements()[i], result);
+ }
+
+ return result + "}";
+ }
+
+ public List getWarnings() {
+ return m_warnings;
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java
new file mode 100644
index 0000000..483f088
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java
@@ -0,0 +1,204 @@
+/*
+ * 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.manipulator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parse on OSGi Manifest clause.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class QuotedTokenizer {
+ /**
+ * String to parse.
+ */
+ String m_string;
+
+ /**
+ * Index.
+ */
+ int m_index = 0;
+
+ /**
+ * Default spearators to use.
+ */
+ String m_separators;
+
+ /**
+ * Does the tokenizer returns token.
+ */
+ boolean m_returnTokens;
+
+ /**
+ * Does the tokenizer should ignore white space.
+ */
+ boolean m_ignoreWhiteSpace = true;
+
+ /**
+ * Peek.
+ */
+ String m_peek;
+
+ /**
+ * Separator.
+ */
+ char m_separator;
+
+ /**
+ * Constructors.
+ * @param string : input String.
+ * @param separators : separators.
+ * @param returnTokens : should the tokenizer return tokens ?
+ */
+ public QuotedTokenizer(String string, String separators, boolean returnTokens) {
+ if (string == null) {
+ throw new IllegalArgumentException("string argument must be not null");
+ }
+ this.m_string = string;
+ this.m_separators = separators;
+ this.m_returnTokens = returnTokens;
+ }
+
+ /**
+ * Constructors.
+ * Fixe returnTokens to false.
+ * @param string : input String.
+ * @param separators : separators
+ */
+ public QuotedTokenizer(String string, String separators) {
+ this(string, separators, false);
+ }
+
+ /**
+ * Get the next token.
+ * @param separators : separators to used.
+ * @return : the next token.
+ */
+ public String nextToken(String separators) {
+ m_separator = 0;
+ if (m_peek != null) {
+ String tmp = m_peek;
+ m_peek = null;
+ return tmp;
+ }
+
+ if (m_index == m_string.length()) { return null; }
+
+ StringBuffer sb = new StringBuffer();
+
+ while (m_index < m_string.length()) {
+ char c = m_string.charAt(m_index++);
+
+ if (Character.isWhitespace(c)) {
+ if (m_index == m_string.length()) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (separators.indexOf(c) >= 0) {
+ if (m_returnTokens) {
+ m_peek = Character.toString(c);
+ } else {
+ m_separator = c;
+ }
+ break;
+ }
+
+ switch (c) {
+ case '"':
+ case '\'':
+ quotedString(sb, c);
+ break;
+
+ default:
+ sb.append(c);
+ }
+ }
+ String result = sb.toString().trim();
+ if (result.length() == 0 && m_index == m_string.length()) { return null; }
+ return result;
+ }
+
+ /**
+ * Get the next token.
+ * Used the defined separators.
+ * @return the next token.
+ */
+ public String nextToken() {
+ return nextToken(m_separators);
+ }
+
+ /**
+ * Append the next quote to the given String Buffer.
+ * @param sb : accumulator.
+ * @param c : quote.
+ */
+ private void quotedString(StringBuffer sb, char c) {
+ char quote = c;
+ while (m_index < m_string.length()) {
+ c = m_string.charAt(m_index++);
+ if (c == quote) { break; }
+ if (c == '\\' && m_index < m_string.length() && m_string.charAt(m_index + 1) == quote) {
+ c = m_string.charAt(m_index++);
+ }
+ sb.append(c);
+ }
+ }
+
+ public String[] getTokens() {
+ return getTokens(0);
+ }
+
+ /**
+ * Get the list of tokens.
+ * @param cnt : array length.
+ * @return : the array of token.
+ */
+ private String[] getTokens(int cnt) {
+ String token = nextToken();
+ if (token == null) {
+ return new String[cnt];
+ }
+
+ String[] result = getTokens(cnt + 1);
+ result[cnt] = token;
+ return result;
+ }
+
+ public char getSeparator() {
+ return m_separator;
+ }
+
+ /**
+ * Get token list.
+ * @return the list of token.
+ */
+ public List getTokenSet() {
+ List list = new ArrayList();
+ String token = nextToken();
+ while (token != null) {
+ list.add(token);
+ token = nextToken();
+ }
+ return list;
+ }
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java
new file mode 100644
index 0000000..cee043d
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.xml.parser;
+
+/**
+ * Exceptions thrown by parsers.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ParseException extends Exception {
+
+ /**
+ * serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Parsing error.
+ * @param msg : the error emssage.
+ */
+ public ParseException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java
new file mode 100644
index 0000000..fa4fd1c
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java
@@ -0,0 +1,311 @@
+/*
+ * 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.xml.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * XML Metadata parser.
+ *
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class XMLMetadataParser implements ContentHandler {
+
+ /**
+ * Element of the metadata.
+ */
+ private Element[] m_elements = new Element[0];
+
+ /**
+ * Get componenet type metadata. (both component and composite)
+ *
+ * @return a components metadata
+ * @throws ParseException
+ * when an error occurs in the xml parsing
+ */
+ public Element[] getComponentsMetadata() throws ParseException {
+ Element[] comp = m_elements[0].getElements("Component");
+ Element[] compo = m_elements[0].getElements("Composite");
+ Element[] metadata = new Element[comp.length + compo.length];
+ int l = 0;
+ for (int i = 0; i < comp.length; i++) {
+ metadata[l] = comp[i];
+ l++;
+ }
+ for (int i = 0; i < compo.length; i++) {
+ metadata[l] = compo[i];
+ l++;
+ }
+ return metadata;
+ }
+
+ /**
+ * Get packages referenced by composite.
+ *
+ * @return the list of referenced packages.
+ */
+ public List getReferredPackages() {
+ List referred = new ArrayList();
+ Element[] compo = m_elements[0].getElements("Composite");
+ for (int i = 0; i < compo.length; i++) {
+ for (int j = 0; j < compo[i].getElements().length; j++) {
+ if (compo[i].getElements()[j].containsAttribute("specification")) {
+ String p = compo[i].getElements()[j].getAttribute("specification");
+ int last = p.lastIndexOf('.');
+ if (last != -1) {
+ referred.add(p.substring(0, last));
+ }
+ }
+ }
+ }
+ return referred;
+ }
+
+ /**
+ * Get parsed metadata.
+ * The document must be parsed before calling this method.
+ * @return : all the metadata.
+ * @throws ParseException : occurs if an error occurs during the parsing.
+ */
+ public Element[] getMetadata() throws ParseException {
+ Element[] comp = m_elements[0].getElements("Component");
+ Element[] compo = m_elements[0].getElements("Composite");
+ Element[] conf = m_elements[0].getElements("Instance");
+ Element[] metadata = new Element[comp.length + conf.length + compo.length];
+ int l = 0;
+ for (int i = 0; i < comp.length; i++) {
+ metadata[l] = comp[i];
+ l++;
+ }
+ for (int i = 0; i < compo.length; i++) {
+ metadata[l] = compo[i];
+ l++;
+ }
+ for (int i = 0; i < conf.length; i++) {
+ metadata[l] = conf[i];
+ l++;
+ }
+ return metadata;
+ }
+
+
+ /**
+ * Characters.
+ * @param ch : character
+ * @param start : start
+ * @param length : length
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ // NOTHING TO DO
+
+ }
+
+
+ /**
+ * End the document.
+ * @throws SAXException : can never occrus.
+ * @see org.xml.sax.ContentHandler#endDocument()
+ */
+ public void endDocument() throws SAXException {
+ }
+
+
+ /**
+ * End of an element.
+ * @param namespaceURI : element namespace
+ * @param localName : local name
+ * @param qName : qualified name
+ * @throws SAXException : occurs when the element is malformed
+ * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ // Get the last element of the list
+ Element lastElement = removeLastElement();
+
+ // Check if the name is consitent with the name of this end tag
+ if (!lastElement.getName().equalsIgnoreCase(qName) && !lastElement.getNameSpace().equals(namespaceURI)) {
+ throw new SAXException("Parse error when ending an element : " + qName + " [" + namespaceURI + "]");
+ }
+
+ // The name is consitent
+ // Add this element last element with if it is not the root
+ if (m_elements.length != 0) {
+ Element newQueue = m_elements[m_elements.length - 1];
+ newQueue.addElement(lastElement);
+ } else {
+ // It is the last element
+ addElement(lastElement);
+ }
+
+ }
+
+ /**
+ * End prefix mapping.
+ * @param prefix : ended prefix
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
+ */
+ public void endPrefixMapping(String prefix) throws SAXException {
+ // NOTHING TO DO
+ }
+
+
+ /**
+ * Ignore whitespace.
+ * @param ch : character
+ * @param start : start
+ * @param length : length
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+ */
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ // NOTHING TO DO
+ }
+
+ /**
+ * Process an instruction.
+ * @param target : target
+ * @param data : data
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+ */
+ public void processingInstruction(String target, String data) throws SAXException {
+ // DO NOTHING
+ }
+
+ /**
+ * Set Document locator.
+ * @param locator : new locator.
+ * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+ */
+ public void setDocumentLocator(Locator locator) {
+ // NOTHING TO DO
+
+ }
+
+ /**
+ * Skipped entity.
+ * @param name : name.
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+ */
+ public void skippedEntity(String name) throws SAXException {
+ // NOTHING TO DO
+
+ }
+
+ /**
+ * Start a document.
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#startDocument()
+ */
+ public void startDocument() throws SAXException {
+ }
+
+
+ /**
+ * Start an element.
+ * @param namespaceURI : element namespace.
+ * @param localName : local element.
+ * @param qName : qualified name.
+ * @param atts : attribute
+ * @throws SAXException : occurs if the element cannot be parsed correctly.
+ * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+ */
+ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+ Element elem = new Element(localName, namespaceURI);
+
+ for (int i = 0; i < atts.getLength(); i++) {
+ String name = (String) atts.getLocalName(i);
+ String ns = (String) atts.getURI(i);
+ String value = (String) atts.getValue(i);
+ Attribute att = new Attribute(name, ns, value);
+ elem.addAttribute(att);
+ }
+
+ addElement(elem);
+
+ }
+
+ /**
+ * Start a prefix mapping.
+ * @param prefix : prefix.
+ * @param uri : uri.
+ * @throws SAXException : can never occurs.
+ * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+ */
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ // NOTHING TO DO
+
+ }
+
+ /**
+ * Add an element.
+ * @param elem : the element to add
+ */
+ private void addElement(Element elem) {
+ for (int i = 0; (m_elements != null) && (i < m_elements.length); i++) {
+ if (m_elements[i] == elem) {
+ return;
+ }
+ }
+
+ if (m_elements != null) {
+ Element[] newElementsList = new Element[m_elements.length + 1];
+ System.arraycopy(m_elements, 0, newElementsList, 0, m_elements.length);
+ newElementsList[m_elements.length] = elem;
+ m_elements = newElementsList;
+ } else {
+ m_elements = new Element[] { elem };
+ }
+ }
+
+ /**
+ * Remove an element.
+ * @return : the removed element.
+ */
+ private Element removeLastElement() {
+ int idx = -1;
+ idx = m_elements.length - 1;
+ Element last = m_elements[idx];
+ if (idx >= 0) {
+ if ((m_elements.length - 1) == 0) {
+ // It is the last element of the list;
+ m_elements = new Element[0];
+ } else {
+ // Remove the last element of the list :
+ Element[] newElementsList = new Element[m_elements.length - 1];
+ System.arraycopy(m_elements, 0, newElementsList, 0, idx);
+ m_elements = newElementsList;
+ }
+ }
+ return last;
+ }
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java
new file mode 100644
index 0000000..f4edfa4
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java
@@ -0,0 +1,650 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link org.objectweb.asm.MethodAdapter} to insert before, after and around
+ * advices in methods and constructors. <p> The behavior for constructors is
+ * like this: <ol>
+ *
+ * <li>as long as the INVOKESPECIAL for the object initialization has not been
+ * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
+ *
+ * <li>when this one is reached, it is only added in the ctor code visitor and
+ * a JP invoke is added</li>
+ *
+ * <li>after that, only the other code visitor receives the instructions</li>
+ *
+ * </ol>
+ *
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
+{
+ private static final Object THIS = new Object();
+ private static final Object OTHER = new Object();
+
+ protected int methodAccess;
+ protected String methodDesc;
+
+ private boolean constructor;
+ private boolean superInitialized;
+ private ArrayList stackFrame;
+ private HashMap branches;
+
+ /**
+ * Creates a new {@link AdviceAdapter}.
+ *
+ * @param mv the method visitor to which this adapter delegates calls.
+ * @param access the method's access flags (see {@link Opcodes}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ */
+ public AdviceAdapter(
+ final MethodVisitor mv,
+ final int access,
+ final String name,
+ final String desc)
+ {
+ super(mv, access, name, desc);
+ methodAccess = access;
+ methodDesc = desc;
+
+ constructor = "<init>".equals(name);
+ }
+
+ public void visitCode() {
+ mv.visitCode();
+ if (!constructor) {
+ superInitialized = true;
+ onMethodEnter();
+ } else {
+ stackFrame = new ArrayList();
+ branches = new HashMap();
+ }
+ }
+
+ public void visitLabel(final Label label) {
+ mv.visitLabel(label);
+
+ if (constructor && branches != null) {
+ ArrayList frame = (ArrayList) branches.get(label);
+ if (frame != null) {
+ stackFrame = frame;
+ branches.remove(label);
+ }
+ }
+ }
+
+ public void visitInsn(final int opcode) {
+ if (constructor) {
+ switch (opcode) {
+ case RETURN: // empty stack
+ onMethodExit(opcode);
+ break;
+
+ case IRETURN: // 1 before n/a after
+ case FRETURN: // 1 before n/a after
+ case ARETURN: // 1 before n/a after
+ case ATHROW: // 1 before n/a after
+ popValue();
+ popValue();
+ onMethodExit(opcode);
+ break;
+
+ case LRETURN: // 2 before n/a after
+ case DRETURN: // 2 before n/a after
+ popValue();
+ popValue();
+ onMethodExit(opcode);
+ break;
+
+ case NOP:
+ case LALOAD: // remove 2 add 2
+ case DALOAD: // remove 2 add 2
+ case LNEG:
+ case DNEG:
+ case FNEG:
+ case INEG:
+ case L2D:
+ case D2L:
+ case F2I:
+ case I2B:
+ case I2C:
+ case I2S:
+ case I2F:
+ case Opcodes.ARRAYLENGTH:
+ break;
+
+ case ACONST_NULL:
+ case ICONST_M1:
+ case ICONST_0:
+ case ICONST_1:
+ case ICONST_2:
+ case ICONST_3:
+ case ICONST_4:
+ case ICONST_5:
+ case FCONST_0:
+ case FCONST_1:
+ case FCONST_2:
+ case F2L: // 1 before 2 after
+ case F2D:
+ case I2L:
+ case I2D:
+ pushValue(OTHER);
+ break;
+
+ case LCONST_0:
+ case LCONST_1:
+ case DCONST_0:
+ case DCONST_1:
+ pushValue(OTHER);
+ pushValue(OTHER);
+ break;
+
+ case IALOAD: // remove 2 add 1
+ case FALOAD: // remove 2 add 1
+ case AALOAD: // remove 2 add 1
+ case BALOAD: // remove 2 add 1
+ case CALOAD: // remove 2 add 1
+ case SALOAD: // remove 2 add 1
+ case POP:
+ case IADD:
+ case FADD:
+ case ISUB:
+ case LSHL: // 3 before 2 after
+ case LSHR: // 3 before 2 after
+ case LUSHR: // 3 before 2 after
+ case L2I: // 2 before 1 after
+ case L2F: // 2 before 1 after
+ case D2I: // 2 before 1 after
+ case D2F: // 2 before 1 after
+ case FSUB:
+ case FMUL:
+ case FDIV:
+ case FREM:
+ case FCMPL: // 2 before 1 after
+ case FCMPG: // 2 before 1 after
+ case IMUL:
+ case IDIV:
+ case IREM:
+ case ISHL:
+ case ISHR:
+ case IUSHR:
+ case IAND:
+ case IOR:
+ case IXOR:
+ case MONITORENTER:
+ case MONITOREXIT:
+ popValue();
+ break;
+
+ case POP2:
+ case LSUB:
+ case LMUL:
+ case LDIV:
+ case LREM:
+ case LADD:
+ case LAND:
+ case LOR:
+ case LXOR:
+ case DADD:
+ case DMUL:
+ case DSUB:
+ case DDIV:
+ case DREM:
+ popValue();
+ popValue();
+ break;
+
+ case IASTORE:
+ case FASTORE:
+ case AASTORE:
+ case BASTORE:
+ case CASTORE:
+ case SASTORE:
+ case LCMP: // 4 before 1 after
+ case DCMPL:
+ case DCMPG:
+ popValue();
+ popValue();
+ popValue();
+ break;
+
+ case LASTORE:
+ case DASTORE:
+ popValue();
+ popValue();
+ popValue();
+ popValue();
+ break;
+
+ case DUP:
+ pushValue(peekValue());
+ break;
+
+ case DUP_X1:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o1);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP_X2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ pushValue(o1);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2_X1:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2_X2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ Object o4 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o4);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case SWAP: {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o1);
+ pushValue(o2);
+ }
+ break;
+ }
+ } else {
+ switch (opcode) {
+ case RETURN:
+ case IRETURN:
+ case FRETURN:
+ case ARETURN:
+ case LRETURN:
+ case DRETURN:
+ case ATHROW:
+ onMethodExit(opcode);
+ break;
+ }
+ }
+ mv.visitInsn(opcode);
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ super.visitVarInsn(opcode, var);
+
+ if (constructor) {
+ switch (opcode) {
+ case ILOAD:
+ case FLOAD:
+ pushValue(OTHER);
+ break;
+ case LLOAD:
+ case DLOAD:
+ pushValue(OTHER);
+ pushValue(OTHER);
+ break;
+ case ALOAD:
+ pushValue(var == 0 ? THIS : OTHER);
+ break;
+ case ASTORE:
+ case ISTORE:
+ case FSTORE:
+ popValue();
+ break;
+ case LSTORE:
+ case DSTORE:
+ popValue();
+ popValue();
+ break;
+ }
+ }
+ }
+
+ public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ mv.visitFieldInsn(opcode, owner, name, desc);
+
+ if (constructor) {
+ char c = desc.charAt(0);
+ boolean longOrDouble = c == 'J' || c == 'D';
+ switch (opcode) {
+ case GETSTATIC:
+ pushValue(OTHER);
+ if (longOrDouble) {
+ pushValue(OTHER);
+ }
+ break;
+ case PUTSTATIC:
+ popValue();
+ if (longOrDouble) {
+ popValue();
+ }
+ break;
+ case PUTFIELD:
+ popValue();
+ if (longOrDouble) {
+ popValue();
+ popValue();
+ }
+ break;
+ // case GETFIELD:
+ default:
+ if (longOrDouble) {
+ pushValue(OTHER);
+ }
+ }
+ }
+ }
+
+ public void visitIntInsn(final int opcode, final int operand) {
+ mv.visitIntInsn(opcode, operand);
+
+ if (constructor && opcode!=NEWARRAY) {
+ pushValue(OTHER);
+ }
+ }
+
+ public void visitLdcInsn(final Object cst) {
+ mv.visitLdcInsn(cst);
+
+ if (constructor) {
+ pushValue(OTHER);
+ if (cst instanceof Double || cst instanceof Long) {
+ pushValue(OTHER);
+ }
+ }
+ }
+
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ mv.visitMultiANewArrayInsn(desc, dims);
+
+ if (constructor) {
+ for (int i = 0; i < dims; i++) {
+ popValue();
+ }
+ pushValue(OTHER);
+ }
+ }
+
+ public void visitTypeInsn(final int opcode, final String name) {
+ mv.visitTypeInsn(opcode, name);
+
+ // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
+ if (constructor && opcode == NEW) {
+ pushValue(OTHER);
+ }
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+
+ if (constructor) {
+ Type[] types = Type.getArgumentTypes(desc);
+ for (int i = 0; i < types.length; i++) {
+ popValue();
+ if (types[i].getSize() == 2) {
+ popValue();
+ }
+ }
+ switch (opcode) {
+ // case INVOKESTATIC:
+ // break;
+
+ case INVOKEINTERFACE:
+ case INVOKEVIRTUAL:
+ popValue(); // objectref
+ break;
+
+ case INVOKESPECIAL:
+ Object type = popValue(); // objectref
+ if (type == THIS && !superInitialized) {
+ onMethodEnter();
+ superInitialized = true;
+ // once super has been initialized it is no longer
+ // necessary to keep track of stack state
+ constructor = false;
+ }
+ break;
+ }
+
+ Type returnType = Type.getReturnType(desc);
+ if (returnType != Type.VOID_TYPE) {
+ pushValue(OTHER);
+ if (returnType.getSize() == 2) {
+ pushValue(OTHER);
+ }
+ }
+ }
+ }
+
+ public void visitJumpInsn(final int opcode, final Label label) {
+ mv.visitJumpInsn(opcode, label);
+
+ if (constructor) {
+ switch (opcode) {
+ case IFEQ:
+ case IFNE:
+ case IFLT:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ case IFNULL:
+ case IFNONNULL:
+ popValue();
+ break;
+
+ case IF_ICMPEQ:
+ case IF_ICMPNE:
+ case IF_ICMPLT:
+ case IF_ICMPGE:
+ case IF_ICMPGT:
+ case IF_ICMPLE:
+ case IF_ACMPEQ:
+ case IF_ACMPNE:
+ popValue();
+ popValue();
+ break;
+
+ case JSR:
+ pushValue(OTHER);
+ break;
+ }
+ addBranch(label);
+ }
+ }
+
+ public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels)
+ {
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+
+ if (constructor) {
+ popValue();
+ addBranches(dflt, labels);
+ }
+ }
+
+ public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels)
+ {
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+
+ if (constructor) {
+ popValue();
+ addBranches(dflt, labels);
+ }
+ }
+
+ private void addBranches(final Label dflt, final Label[] labels) {
+ addBranch(dflt);
+ for (int i = 0; i < labels.length; i++) {
+ addBranch(labels[i]);
+ }
+ }
+
+ private void addBranch(final Label label) {
+ if (branches.containsKey(label)) {
+ return;
+ }
+ ArrayList frame = new ArrayList();
+ frame.addAll(stackFrame);
+ branches.put(label, frame);
+ }
+
+ private Object popValue() {
+ return stackFrame.remove(stackFrame.size() - 1);
+ }
+
+ private Object peekValue() {
+ return stackFrame.get(stackFrame.size() - 1);
+ }
+
+ private void pushValue(final Object o) {
+ stackFrame.add(o);
+ }
+
+ /**
+ * Called at the beginning of the method or after super class class call in
+ * the constructor. <br><br>
+ *
+ * <i>Custom code can use or change all the local variables, but should not
+ * change state of the stack.</i>
+ */
+ protected abstract void onMethodEnter();
+
+ /**
+ * Called before explicit exit from the method using either return or throw.
+ * Top element on the stack contains the return value or exception instance.
+ * For example:
+ *
+ * <pre>
+ * public void onMethodExit(int opcode) {
+ * if(opcode==RETURN) {
+ * visitInsn(ACONST_NULL);
+ * } else if(opcode==ARETURN || opcode==ATHROW) {
+ * dup();
+ * } else {
+ * if(opcode==LRETURN || opcode==DRETURN) {
+ * dup2();
+ * } else {
+ * dup();
+ * }
+ * box(Type.getReturnType(this.methodDesc));
+ * }
+ * visitIntInsn(SIPUSH, opcode);
+ * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+ * }
+ *
+ * // an actual call back method
+ * public static void onExit(int opcode, Object param) {
+ * ...
+ * </pre>
+ *
+ * <br><br>
+ *
+ * <i>Custom code can use or change all the local variables, but should not
+ * change state of the stack.</i>
+ *
+ * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
+ * DRETURN or ATHROW
+ *
+ */
+ protected abstract void onMethodExit(int opcode);
+
+ // TODO onException, onMethodCall
+
+}
diff --git a/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/GeneratorAdapter.java b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/GeneratorAdapter.java
new file mode 100644
index 0000000..972b2ff
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/GeneratorAdapter.java
@@ -0,0 +1,1435 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link org.objectweb.asm.MethodAdapter} with convenient methods to generate
+ * code. For example, using this adapter, the class below
+ *
+ * <pre>
+ * public class Example {
+ * public static void main(String[] args) {
+ * System.out.println("Hello world!");
+ * }
+ * }
+ * </pre>
+ *
+ * can be generated as follows:
+ *
+ * <pre>
+ * ClassWriter cw = new ClassWriter(true);
+ * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
+ *
+ * Method m = Method.getMethod("void <init> ()");
+ * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
+ * mg.loadThis();
+ * mg.invokeConstructor(Type.getType(Object.class), m);
+ * mg.returnValue();
+ * mg.endMethod();
+ *
+ * m = Method.getMethod("void main (String[])");
+ * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
+ * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
+ * mg.push("Hello world!");
+ * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));
+ * mg.returnValue();
+ * mg.endMethod();
+ *
+ * cw.visitEnd();
+ * </pre>
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class GeneratorAdapter extends LocalVariablesSorter {
+
+ private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
+
+ private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
+
+ private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
+
+ private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
+
+ private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
+
+ private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
+
+ private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
+
+ private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
+
+ private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
+
+ private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
+ private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
+
+ private final static Method CHAR_VALUE = Method.getMethod("char charValue()");
+
+ private final static Method INT_VALUE = Method.getMethod("int intValue()");
+
+ private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()");
+
+ private final static Method LONG_VALUE = Method.getMethod("long longValue()");
+
+ private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int ADD = Opcodes.IADD;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int SUB = Opcodes.ISUB;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int MUL = Opcodes.IMUL;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int DIV = Opcodes.IDIV;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int REM = Opcodes.IREM;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int NEG = Opcodes.INEG;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int SHL = Opcodes.ISHL;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int SHR = Opcodes.ISHR;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int USHR = Opcodes.IUSHR;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int AND = Opcodes.IAND;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int OR = Opcodes.IOR;
+
+ /**
+ * Constant for the {@link #math math} method.
+ */
+ public final static int XOR = Opcodes.IXOR;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int EQ = Opcodes.IFEQ;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int NE = Opcodes.IFNE;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int LT = Opcodes.IFLT;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int GE = Opcodes.IFGE;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int GT = Opcodes.IFGT;
+
+ /**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+ public final static int LE = Opcodes.IFLE;
+
+ /**
+ * Access flags of the method visited by this adapter.
+ */
+ private final int access;
+
+ /**
+ * Return type of the method visited by this adapter.
+ */
+ private final Type returnType;
+
+ /**
+ * Argument types of the method visited by this adapter.
+ */
+ private final Type[] argumentTypes;
+
+ /**
+ * Types of the local variables of the method visited by this adapter.
+ */
+ private final List localTypes = new ArrayList();
+
+ /**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @param mv the method visitor to which this adapter delegates calls.
+ * @param access the method's access flags (see {@link Opcodes}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ */
+ public GeneratorAdapter(
+ final MethodVisitor mv,
+ final int access,
+ final String name,
+ final String desc)
+ {
+ super(access, desc, mv);
+ this.access = access;
+ this.returnType = Type.getReturnType(desc);
+ this.argumentTypes = Type.getArgumentTypes(desc);
+ }
+
+ /**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param method the adapted method.
+ * @param mv the method visitor to which this adapter delegates calls.
+ */
+ public GeneratorAdapter(
+ final int access,
+ final Method method,
+ final MethodVisitor mv)
+ {
+ super(access, method.getDescriptor(), mv);
+ this.access = access;
+ this.returnType = method.getReturnType();
+ this.argumentTypes = method.getArgumentTypes();
+ }
+
+ /**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param method the adapted method.
+ * @param signature the signature of the adapted method (may be
+ * <tt>null</tt>).
+ * @param exceptions the exceptions thrown by the adapted method (may be
+ * <tt>null</tt>).
+ * @param cv the class visitor to which this adapter delegates calls.
+ */
+ public GeneratorAdapter(
+ final int access,
+ final Method method,
+ final String signature,
+ final Type[] exceptions,
+ final ClassVisitor cv)
+ {
+ this(access, method, cv.visitMethod(access,
+ method.getName(),
+ method.getDescriptor(),
+ signature,
+ getInternalNames(exceptions)));
+ }
+
+ /**
+ * Returns the internal names of the given types.
+ *
+ * @param types a set of types.
+ * @return the internal names of the given types.
+ */
+ private static String[] getInternalNames(final Type[] types) {
+ if (types == null) {
+ return null;
+ }
+ String[] names = new String[types.length];
+ for (int i = 0; i < names.length; ++i) {
+ names[i] = types[i].getInternalName();
+ }
+ return names;
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to push constants on the stack
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final boolean value) {
+ push(value ? 1 : 0);
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final int value) {
+ if (value >= -1 && value <= 5) {
+ mv.visitInsn(Opcodes.ICONST_0 + value);
+ } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
+ mv.visitIntInsn(Opcodes.BIPUSH, value);
+ } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
+ mv.visitIntInsn(Opcodes.SIPUSH, value);
+ } else {
+ mv.visitLdcInsn(new Integer(value));
+ }
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final long value) {
+ if (value == 0L || value == 1L) {
+ mv.visitInsn(Opcodes.LCONST_0 + (int) value);
+ } else {
+ mv.visitLdcInsn(new Long(value));
+ }
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final float value) {
+ int bits = Float.floatToIntBits(value);
+ if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
+ mv.visitInsn(Opcodes.FCONST_0 + (int) value);
+ } else {
+ mv.visitLdcInsn(new Float(value));
+ }
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final double value) {
+ long bits = Double.doubleToLongBits(value);
+ if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
+ mv.visitInsn(Opcodes.DCONST_0 + (int) value);
+ } else {
+ mv.visitLdcInsn(new Double(value));
+ }
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack. May be <tt>null</tt>.
+ */
+ public void push(final String value) {
+ if (value == null) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ }
+
+ /**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+ public void push(final Type value) {
+ if (value == null) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to load and store method arguments
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the index of the given method argument in the frame's local
+ * variables array.
+ *
+ * @param arg the index of a method argument.
+ * @return the index of the given method argument in the frame's local
+ * variables array.
+ */
+ private int getArgIndex(final int arg) {
+ int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
+ for (int i = 0; i < arg; i++) {
+ index += argumentTypes[i].getSize();
+ }
+ return index;
+ }
+
+ /**
+ * Generates the instruction to push a local variable on the stack.
+ *
+ * @param type the type of the local variable to be loaded.
+ * @param index an index in the frame's local variables array.
+ */
+ private void loadInsn(final Type type, final int index) {
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in a local
+ * variable.
+ *
+ * @param type the type of the local variable to be stored.
+ * @param index an index in the frame's local variables array.
+ */
+ private void storeInsn(final Type type, final int index) {
+ mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
+ }
+
+ /**
+ * Generates the instruction to load 'this' on the stack.
+ */
+ public void loadThis() {
+ if ((access & Opcodes.ACC_STATIC) != 0) {
+ throw new IllegalStateException("no 'this' pointer within static method");
+ }
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ }
+
+ /**
+ * Generates the instruction to load the given method argument on the stack.
+ *
+ * @param arg the index of a method argument.
+ */
+ public void loadArg(final int arg) {
+ loadInsn(argumentTypes[arg], getArgIndex(arg));
+ }
+
+ /**
+ * Generates the instructions to load the given method arguments on the
+ * stack.
+ *
+ * @param arg the index of the first method argument to be loaded.
+ * @param count the number of method arguments to be loaded.
+ */
+ public void loadArgs(final int arg, final int count) {
+ int index = getArgIndex(arg);
+ for (int i = 0; i < count; ++i) {
+ Type t = argumentTypes[arg + i];
+ loadInsn(t, index);
+ index += t.getSize();
+ }
+ }
+
+ /**
+ * Generates the instructions to load all the method arguments on the stack.
+ */
+ public void loadArgs() {
+ loadArgs(0, argumentTypes.length);
+ }
+
+ /**
+ * Generates the instructions to load all the method arguments on the stack,
+ * as a single object array.
+ */
+ public void loadArgArray() {
+ push(argumentTypes.length);
+ newArray(OBJECT_TYPE);
+ for (int i = 0; i < argumentTypes.length; i++) {
+ dup();
+ push(i);
+ loadArg(i);
+ box(argumentTypes[i]);
+ arrayStore(OBJECT_TYPE);
+ }
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in the given
+ * method argument.
+ *
+ * @param arg the index of a method argument.
+ */
+ public void storeArg(final int arg) {
+ storeInsn(argumentTypes[arg], getArgIndex(arg));
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to load and store local variables
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns the type of the given local variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @return the type of the given local variable.
+ */
+ public Type getLocalType(final int local) {
+ return (Type) localTypes.get(local - firstLocal);
+ }
+
+ protected void setLocalType(final int local, final Type type) {
+ int index = local - firstLocal;
+ while (localTypes.size() < index + 1) {
+ localTypes.add(null);
+ }
+ localTypes.set(index, type);
+ }
+
+ /**
+ * Generates the instruction to load the given local variable on the stack.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+ public void loadLocal(final int local) {
+ loadInsn(getLocalType(local), local);
+ }
+
+ /**
+ * Generates the instruction to load the given local variable on the stack.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @param type the type of this local variable.
+ */
+ public void loadLocal(final int local, final Type type) {
+ setLocalType(local, type);
+ loadInsn(type, local);
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in the given local
+ * variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+ public void storeLocal(final int local) {
+ storeInsn(getLocalType(local), local);
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in the given local
+ * variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @param type the type of this local variable.
+ */
+ public void storeLocal(final int local, final Type type) {
+ setLocalType(local, type);
+ storeInsn(type, local);
+ }
+
+ /**
+ * Generates the instruction to load an element from an array.
+ *
+ * @param type the type of the array element to be loaded.
+ */
+ public void arrayLoad(final Type type) {
+ mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
+ }
+
+ /**
+ * Generates the instruction to store an element in an array.
+ *
+ * @param type the type of the array element to be stored.
+ */
+ public void arrayStore(final Type type) {
+ mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to manage the stack
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates a POP instruction.
+ */
+ public void pop() {
+ mv.visitInsn(Opcodes.POP);
+ }
+
+ /**
+ * Generates a POP2 instruction.
+ */
+ public void pop2() {
+ mv.visitInsn(Opcodes.POP2);
+ }
+
+ /**
+ * Generates a DUP instruction.
+ */
+ public void dup() {
+ mv.visitInsn(Opcodes.DUP);
+ }
+
+ /**
+ * Generates a DUP2 instruction.
+ */
+ public void dup2() {
+ mv.visitInsn(Opcodes.DUP2);
+ }
+
+ /**
+ * Generates a DUP_X1 instruction.
+ */
+ public void dupX1() {
+ mv.visitInsn(Opcodes.DUP_X1);
+ }
+
+ /**
+ * Generates a DUP_X2 instruction.
+ */
+ public void dupX2() {
+ mv.visitInsn(Opcodes.DUP_X2);
+ }
+
+ /**
+ * Generates a DUP2_X1 instruction.
+ */
+ public void dup2X1() {
+ mv.visitInsn(Opcodes.DUP2_X1);
+ }
+
+ /**
+ * Generates a DUP2_X2 instruction.
+ */
+ public void dup2X2() {
+ mv.visitInsn(Opcodes.DUP2_X2);
+ }
+
+ /**
+ * Generates a SWAP instruction.
+ */
+ public void swap() {
+ mv.visitInsn(Opcodes.SWAP);
+ }
+
+ /**
+ * Generates the instructions to swap the top two stack values.
+ *
+ * @param prev type of the top - 1 stack value.
+ * @param type type of the top stack value.
+ */
+ public void swap(final Type prev, final Type type) {
+ if (type.getSize() == 1) {
+ if (prev.getSize() == 1) {
+ swap(); // same as dupX1(), pop();
+ } else {
+ dupX2();
+ pop();
+ }
+ } else {
+ if (prev.getSize() == 1) {
+ dup2X1();
+ pop2();
+ } else {
+ dup2X2();
+ pop2();
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to do mathematical and logical operations
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates the instruction to do the specified mathematical or logical
+ * operation.
+ *
+ * @param op a mathematical or logical operation. Must be one of ADD, SUB,
+ * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
+ * @param type the type of the operand(s) for this operation.
+ */
+ public void math(final int op, final Type type) {
+ mv.visitInsn(type.getOpcode(op));
+ }
+
+ /**
+ * Generates the instructions to compute the bitwise negation of the top
+ * stack value.
+ */
+ public void not() {
+ mv.visitInsn(Opcodes.ICONST_1);
+ mv.visitInsn(Opcodes.IXOR);
+ }
+
+ /**
+ * Generates the instruction to increment the given local variable.
+ *
+ * @param local the local variable to be incremented.
+ * @param amount the amount by which the local variable must be incremented.
+ */
+ public void iinc(final int local, final int amount) {
+ mv.visitIincInsn(local, amount);
+ }
+
+ /**
+ * Generates the instructions to cast a numerical value from one type to
+ * another.
+ *
+ * @param from the type of the top stack value
+ * @param to the type into which this value must be cast.
+ */
+ public void cast(final Type from, final Type to) {
+ if (from != to) {
+ if (from == Type.DOUBLE_TYPE) {
+ if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Opcodes.D2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Opcodes.D2L);
+ } else {
+ mv.visitInsn(Opcodes.D2I);
+ cast(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.FLOAT_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Opcodes.F2D);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Opcodes.F2L);
+ } else {
+ mv.visitInsn(Opcodes.F2I);
+ cast(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.LONG_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Opcodes.L2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Opcodes.L2F);
+ } else {
+ mv.visitInsn(Opcodes.L2I);
+ cast(Type.INT_TYPE, to);
+ }
+ } else {
+ if (to == Type.BYTE_TYPE) {
+ mv.visitInsn(Opcodes.I2B);
+ } else if (to == Type.CHAR_TYPE) {
+ mv.visitInsn(Opcodes.I2C);
+ } else if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Opcodes.I2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Opcodes.I2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Opcodes.I2L);
+ } else if (to == Type.SHORT_TYPE) {
+ mv.visitInsn(Opcodes.I2S);
+ }
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to do boxing and unboxing operations
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates the instructions to box the top stack value. This value is
+ * replaced by its boxed equivalent on top of the stack.
+ *
+ * @param type the type of the top stack value.
+ */
+ public void box(final Type type) {
+ if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
+ return;
+ }
+ if (type == Type.VOID_TYPE) {
+ push((String) null);
+ } else {
+ Type boxed = type;
+ switch (type.getSort()) {
+ case Type.BYTE:
+ boxed = BYTE_TYPE;
+ break;
+ case Type.BOOLEAN:
+ boxed = BOOLEAN_TYPE;
+ break;
+ case Type.SHORT:
+ boxed = SHORT_TYPE;
+ break;
+ case Type.CHAR:
+ boxed = CHARACTER_TYPE;
+ break;
+ case Type.INT:
+ boxed = INTEGER_TYPE;
+ break;
+ case Type.FLOAT:
+ boxed = FLOAT_TYPE;
+ break;
+ case Type.LONG:
+ boxed = LONG_TYPE;
+ break;
+ case Type.DOUBLE:
+ boxed = DOUBLE_TYPE;
+ break;
+ }
+ newInstance(boxed);
+ if (type.getSize() == 2) {
+ // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
+ dupX2();
+ dupX2();
+ pop();
+ } else {
+ // p -> po -> opo -> oop -> o
+ dupX1();
+ swap();
+ }
+ invokeConstructor(boxed, new Method("<init>",
+ Type.VOID_TYPE,
+ new Type[] { type }));
+ }
+ }
+
+ /**
+ * Generates the instructions to unbox the top stack value. This value is
+ * replaced by its unboxed equivalent on top of the stack.
+ *
+ * @param type the type of the top stack value.
+ */
+ public void unbox(final Type type) {
+ Type t = NUMBER_TYPE;
+ Method sig = null;
+ switch (type.getSort()) {
+ case Type.VOID:
+ return;
+ case Type.CHAR:
+ t = CHARACTER_TYPE;
+ sig = CHAR_VALUE;
+ break;
+ case Type.BOOLEAN:
+ t = BOOLEAN_TYPE;
+ sig = BOOLEAN_VALUE;
+ break;
+ case Type.DOUBLE:
+ sig = DOUBLE_VALUE;
+ break;
+ case Type.FLOAT:
+ sig = FLOAT_VALUE;
+ break;
+ case Type.LONG:
+ sig = LONG_VALUE;
+ break;
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ sig = INT_VALUE;
+ }
+ if (sig == null) {
+ checkCast(type);
+ } else {
+ checkCast(t);
+ invokeVirtual(t, sig);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to jump to other instructions
+ // ------------------------------------------------------------------------
+
+ /**
+ * Creates a new {@link Label}.
+ *
+ * @return a new {@link Label}.
+ */
+ public Label newLabel() {
+ return new Label();
+ }
+
+ /**
+ * Marks the current code position with the given label.
+ *
+ * @param label a label.
+ */
+ public void mark(final Label label) {
+ mv.visitLabel(label);
+ }
+
+ /**
+ * Marks the current code position with a new label.
+ *
+ * @return the label that was created to mark the current code position.
+ */
+ public Label mark() {
+ Label label = new Label();
+ mv.visitLabel(label);
+ return label;
+ }
+
+ /**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top two stack values.
+ *
+ * @param type the type of the top two stack values.
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+ public void ifCmp(final Type type, final int mode, final Label label) {
+ int intOp = -1;
+ switch (type.getSort()) {
+ case Type.LONG:
+ mv.visitInsn(Opcodes.LCMP);
+ break;
+ case Type.DOUBLE:
+ mv.visitInsn(Opcodes.DCMPG);
+ break;
+ case Type.FLOAT:
+ mv.visitInsn(Opcodes.FCMPG);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ switch (mode) {
+ case EQ:
+ mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
+ return;
+ case NE:
+ mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
+ return;
+ }
+ throw new IllegalArgumentException("Bad comparison for type "
+ + type);
+ default:
+ switch (mode) {
+ case EQ:
+ intOp = Opcodes.IF_ICMPEQ;
+ break;
+ case NE:
+ intOp = Opcodes.IF_ICMPNE;
+ break;
+ case GE:
+ intOp = Opcodes.IF_ICMPGE;
+ break;
+ case LT:
+ intOp = Opcodes.IF_ICMPLT;
+ break;
+ case LE:
+ intOp = Opcodes.IF_ICMPLE;
+ break;
+ case GT:
+ intOp = Opcodes.IF_ICMPGT;
+ break;
+ }
+ mv.visitJumpInsn(intOp, label);
+ return;
+ }
+ int jumpMode = mode;
+ switch (mode) {
+ case GE:
+ jumpMode = LT;
+ break;
+ case LE:
+ jumpMode = GT;
+ break;
+ }
+ mv.visitJumpInsn(jumpMode, label);
+ }
+
+ /**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top two integer stack values.
+ *
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+ public void ifICmp(final int mode, final Label label) {
+ ifCmp(Type.INT_TYPE, mode, label);
+ }
+
+ /**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top integer stack value with zero.
+ *
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+ public void ifZCmp(final int mode, final Label label) {
+ mv.visitJumpInsn(mode, label);
+ }
+
+ /**
+ * Generates the instruction to jump to the given label if the top stack
+ * value is null.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+ public void ifNull(final Label label) {
+ mv.visitJumpInsn(Opcodes.IFNULL, label);
+ }
+
+ /**
+ * Generates the instruction to jump to the given label if the top stack
+ * value is not null.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+ public void ifNonNull(final Label label) {
+ mv.visitJumpInsn(Opcodes.IFNONNULL, label);
+ }
+
+ /**
+ * Generates the instruction to jump to the given label.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+ public void goTo(final Label label) {
+ mv.visitJumpInsn(Opcodes.GOTO, label);
+ }
+
+ /**
+ * Generates a RET instruction.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+ public void ret(final int local) {
+ mv.visitVarInsn(Opcodes.RET, local);
+ }
+
+ /**
+ * Generates the instructions for a switch statement.
+ *
+ * @param keys the switch case keys.
+ * @param generator a generator to generate the code for the switch cases.
+ */
+ public void tableSwitch(
+ final int[] keys,
+ final TableSwitchGenerator generator)
+ {
+ float density;
+ if (keys.length == 0) {
+ density = 0;
+ } else {
+ density = (float) keys.length
+ / (keys[keys.length - 1] - keys[0] + 1);
+ }
+ tableSwitch(keys, generator, density >= 0.5f);
+ }
+
+ /**
+ * Generates the instructions for a switch statement.
+ *
+ * @param keys the switch case keys.
+ * @param generator a generator to generate the code for the switch cases.
+ * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
+ * <tt>false</tt> to use a LOOKUPSWITCH instruction.
+ */
+ public void tableSwitch(
+ final int[] keys,
+ final TableSwitchGenerator generator,
+ final boolean useTable)
+ {
+ for (int i = 1; i < keys.length; ++i) {
+ if (keys[i] < keys[i - 1]) {
+ throw new IllegalArgumentException("keys must be sorted ascending");
+ }
+ }
+ Label def = newLabel();
+ Label end = newLabel();
+ if (keys.length > 0) {
+ int len = keys.length;
+ int min = keys[0];
+ int max = keys[len - 1];
+ int range = max - min + 1;
+ if (useTable) {
+ Label[] labels = new Label[range];
+ Arrays.fill(labels, def);
+ for (int i = 0; i < len; ++i) {
+ labels[keys[i] - min] = newLabel();
+ }
+ mv.visitTableSwitchInsn(min, max, def, labels);
+ for (int i = 0; i < range; ++i) {
+ Label label = labels[i];
+ if (label != def) {
+ mark(label);
+ generator.generateCase(i + min, end);
+ }
+ }
+ } else {
+ Label[] labels = new Label[len];
+ for (int i = 0; i < len; ++i) {
+ labels[i] = newLabel();
+ }
+ mv.visitLookupSwitchInsn(def, keys, labels);
+ for (int i = 0; i < len; ++i) {
+ mark(labels[i]);
+ generator.generateCase(keys[i], end);
+ }
+ }
+ }
+ mark(def);
+ generator.generateDefault();
+ mark(end);
+ }
+
+ /**
+ * Generates the instruction to return the top stack value to the caller.
+ */
+ public void returnValue() {
+ mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to load and store fields
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates a get field or set field instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param ownerType the class in which the field is defined.
+ * @param name the name of the field.
+ * @param fieldType the type of the field.
+ */
+ private void fieldInsn(
+ final int opcode,
+ final Type ownerType,
+ final String name,
+ final Type fieldType)
+ {
+ mv.visitFieldInsn(opcode,
+ ownerType.getInternalName(),
+ name,
+ fieldType.getDescriptor());
+ }
+
+ /**
+ * Generates the instruction to push the value of a static field on the
+ * stack.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+ public void getStatic(final Type owner, final String name, final Type type)
+ {
+ fieldInsn(Opcodes.GETSTATIC, owner, name, type);
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in a static field.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+ public void putStatic(final Type owner, final String name, final Type type)
+ {
+ fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
+ }
+
+ /**
+ * Generates the instruction to push the value of a non static field on the
+ * stack.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+ public void getField(final Type owner, final String name, final Type type) {
+ fieldInsn(Opcodes.GETFIELD, owner, name, type);
+ }
+
+ /**
+ * Generates the instruction to store the top stack value in a non static
+ * field.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+ public void putField(final Type owner, final String name, final Type type) {
+ fieldInsn(Opcodes.PUTFIELD, owner, name, type);
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to invoke methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates an invoke method instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param type the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+ private void invokeInsn(
+ final int opcode,
+ final Type type,
+ final Method method)
+ {
+ String owner = type.getSort() == Type.ARRAY
+ ? type.getDescriptor()
+ : type.getInternalName();
+ mv.visitMethodInsn(opcode,
+ owner,
+ method.getName(),
+ method.getDescriptor());
+ }
+
+ /**
+ * Generates the instruction to invoke a normal method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+ public void invokeVirtual(final Type owner, final Method method) {
+ invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
+ }
+
+ /**
+ * Generates the instruction to invoke a constructor.
+ *
+ * @param type the class in which the constructor is defined.
+ * @param method the constructor to be invoked.
+ */
+ public void invokeConstructor(final Type type, final Method method) {
+ invokeInsn(Opcodes.INVOKESPECIAL, type, method);
+ }
+
+ /**
+ * Generates the instruction to invoke a static method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+ public void invokeStatic(final Type owner, final Method method) {
+ invokeInsn(Opcodes.INVOKESTATIC, owner, method);
+ }
+
+ /**
+ * Generates the instruction to invoke an interface method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+ public void invokeInterface(final Type owner, final Method method) {
+ invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
+ }
+
+ // ------------------------------------------------------------------------
+ // Instructions to create objects and arrays
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates a type dependent instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param type the instruction's operand.
+ */
+ private void typeInsn(final int opcode, final Type type) {
+ String desc;
+ if (type.getSort() == Type.ARRAY) {
+ desc = type.getDescriptor();
+ } else {
+ desc = type.getInternalName();
+ }
+ mv.visitTypeInsn(opcode, desc);
+ }
+
+ /**
+ * Generates the instruction to create a new object.
+ *
+ * @param type the class of the object to be created.
+ */
+ public void newInstance(final Type type) {
+ typeInsn(Opcodes.NEW, type);
+ }
+
+ /**
+ * Generates the instruction to create a new array.
+ *
+ * @param type the type of the array elements.
+ */
+ public void newArray(final Type type) {
+ int typ;
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ typ = Opcodes.T_BOOLEAN;
+ break;
+ case Type.CHAR:
+ typ = Opcodes.T_CHAR;
+ break;
+ case Type.BYTE:
+ typ = Opcodes.T_BYTE;
+ break;
+ case Type.SHORT:
+ typ = Opcodes.T_SHORT;
+ break;
+ case Type.INT:
+ typ = Opcodes.T_INT;
+ break;
+ case Type.FLOAT:
+ typ = Opcodes.T_FLOAT;
+ break;
+ case Type.LONG:
+ typ = Opcodes.T_LONG;
+ break;
+ case Type.DOUBLE:
+ typ = Opcodes.T_DOUBLE;
+ break;
+ default:
+ typeInsn(Opcodes.ANEWARRAY, type);
+ return;
+ }
+ mv.visitIntInsn(Opcodes.NEWARRAY, typ);
+ }
+
+ // ------------------------------------------------------------------------
+ // Miscelaneous instructions
+ // ------------------------------------------------------------------------
+
+ /**
+ * Generates the instruction to compute the length of an array.
+ */
+ public void arrayLength() {
+ mv.visitInsn(Opcodes.ARRAYLENGTH);
+ }
+
+ /**
+ * Generates the instruction to throw an exception.
+ */
+ public void throwException() {
+ mv.visitInsn(Opcodes.ATHROW);
+ }
+
+ /**
+ * Generates the instructions to create and throw an exception. The
+ * exception class must have a constructor with a single String argument.
+ *
+ * @param type the class of the exception to be thrown.
+ * @param msg the detailed message of the exception.
+ */
+ public void throwException(final Type type, final String msg) {
+ newInstance(type);
+ dup();
+ push(msg);
+ invokeConstructor(type, Method.getMethod("void <init> (String)"));
+ throwException();
+ }
+
+ /**
+ * Generates the instruction to check that the top stack value is of the
+ * given type.
+ *
+ * @param type a class or interface type.
+ */
+ public void checkCast(final Type type) {
+ if (!type.equals(OBJECT_TYPE)) {
+ typeInsn(Opcodes.CHECKCAST, type);
+ }
+ }
+
+ /**
+ * Generates the instruction to test if the top stack value is of the given
+ * type.
+ *
+ * @param type a class or interface type.
+ */
+ public void instanceOf(final Type type) {
+ typeInsn(Opcodes.INSTANCEOF, type);
+ }
+
+ /**
+ * Generates the instruction to get the monitor of the top stack value.
+ */
+ public void monitorEnter() {
+ mv.visitInsn(Opcodes.MONITORENTER);
+ }
+
+ /**
+ * Generates the instruction to release the monitor of the top stack value.
+ */
+ public void monitorExit() {
+ mv.visitInsn(Opcodes.MONITOREXIT);
+ }
+
+ // ------------------------------------------------------------------------
+ // Non instructions
+ // ------------------------------------------------------------------------
+
+ /**
+ * Marks the end of the visited method.
+ */
+ public void endMethod() {
+ if ((access & Opcodes.ACC_ABSTRACT) == 0) {
+ mv.visitMaxs(0, 0);
+ }
+ mv.visitEnd();
+ }
+
+ /**
+ * Marks the start of an exception handler.
+ *
+ * @param start beginning of the exception handler's scope (inclusive).
+ * @param end end of the exception handler's scope (exclusive).
+ * @param exception internal name of the type of exceptions handled by the
+ * handler.
+ */
+ public void catchException(
+ final Label start,
+ final Label end,
+ final Type exception)
+ {
+ mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName());
+ }
+}
diff --git a/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/LocalVariablesSorter.java b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/LocalVariablesSorter.java
new file mode 100644
index 0000000..96c8283
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/LocalVariablesSorter.java
@@ -0,0 +1,314 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link MethodAdapter} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method. It may be used by inheriting from this class, but the preferred way
+ * of using it is via delegation: the next visitor in the chain can indeed add
+ * new locals when needed by calling {@link #newLocal} on this adapter (this
+ * requires a reference back to this {@link LocalVariablesSorter}).
+ *
+ * @author Chris Nokleberg
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodAdapter {
+
+ private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
+ /**
+ * Mapping from old to new local variable indexes. A local variable at index
+ * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
+ * index i of size 2 is remapped to 'mapping[2*i+1]'.
+ */
+ private int[] mapping = new int[40];
+
+ /**
+ * Array used to store stack map local variable types after remapping.
+ */
+ private Object[] newLocals = new Object[20];
+
+ /**
+ * Index of the first local variable, after formal parameters.
+ */
+ protected final int firstLocal;
+
+ /**
+ * Index of the next local variable to be created by {@link #newLocal}.
+ */
+ protected int nextLocal;
+
+ /**
+ * Indicates if at least one local variable has moved due to remapping.
+ */
+ private boolean changed;
+
+ /**
+ * Creates a new {@link LocalVariablesSorter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ * @param mv the method visitor to which this adapter delegates calls.
+ */
+ public LocalVariablesSorter(
+ final int access,
+ final String desc,
+ final MethodVisitor mv)
+ {
+ super(mv);
+ Type[] args = Type.getArgumentTypes(desc);
+ nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
+ for (int i = 0; i < args.length; i++) {
+ nextLocal += args[i].getSize();
+ }
+ firstLocal = nextLocal;
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ Type type;
+ switch (opcode) {
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ type = Type.LONG_TYPE;
+ break;
+
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ type = Type.DOUBLE_TYPE;
+ break;
+
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ type = Type.FLOAT_TYPE;
+ break;
+
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ type = Type.INT_TYPE;
+ break;
+
+ case Opcodes.ALOAD:
+ case Opcodes.ASTORE:
+ type = OBJECT_TYPE;
+ break;
+
+ // case RET:
+ default:
+ type = Type.VOID_TYPE;
+ }
+ mv.visitVarInsn(opcode, remap(var, type));
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ mv.visitMaxs(maxStack, nextLocal);
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
+ int newIndex = remap(index, size);
+ mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
+ }
+
+ public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack)
+ {
+ if (type != Opcodes.F_NEW) { // uncompressed frame
+ throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
+ }
+
+ if (!changed) { // optimization for the case where mapping = identity
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ return;
+ }
+
+ // creates a copy of newLocals
+ Object[] oldLocals = new Object[newLocals.length];
+ System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
+
+ // copies types from 'local' to 'newLocals'
+ // 'newLocals' already contains the variables added with 'newLocal'
+
+ int index = 0; // old local variable index
+ int number = 0; // old local variable number
+ for (; number < nLocal; ++number) {
+ Object t = local[number];
+ int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
+ if (t != Opcodes.TOP) {
+ setFrameLocal(remap(index, size), t);
+ }
+ index += size;
+ }
+
+ // removes TOP after long and double types as well as trailing TOPs
+
+ index = 0;
+ number = 0;
+ for (int i = 0; index < newLocals.length; ++i) {
+ Object t = newLocals[index++];
+ if (t != null && t != Opcodes.TOP) {
+ newLocals[i] = t;
+ number = i + 1;
+ if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
+ index += 1;
+ }
+ } else {
+ newLocals[i] = Opcodes.TOP;
+ }
+ }
+
+ // visits remapped frame
+ mv.visitFrame(type, number, newLocals, nStack, stack);
+
+ // restores original value of 'newLocals'
+ newLocals = oldLocals;
+ }
+
+ // -------------
+
+ /**
+ * Creates a new local variable of the given type.
+ *
+ * @param type the type of the local variable to be created.
+ * @return the identifier of the newly created local variable.
+ */
+ public int newLocal(final Type type) {
+ Object t;
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ t = Opcodes.INTEGER;
+ break;
+ case Type.FLOAT:
+ t = Opcodes.FLOAT;
+ break;
+ case Type.LONG:
+ t = Opcodes.LONG;
+ break;
+ case Type.DOUBLE:
+ t = Opcodes.DOUBLE;
+ break;
+ case Type.ARRAY:
+ t = type.getDescriptor();
+ break;
+ // case Type.OBJECT:
+ default:
+ t = type.getInternalName();
+ break;
+ }
+ int local = nextLocal;
+ setLocalType(local, type);
+ setFrameLocal(local, t);
+ nextLocal += type.getSize();
+ return local;
+ }
+
+ /**
+ * Sets the current type of the given local variable. The default
+ * implementation of this method does nothing.
+ *
+ * @param local a local variable identifier, as returned by {@link #newLocal
+ * newLocal()}.
+ * @param type the type of the value being stored in the local variable
+ */
+ protected void setLocalType(final int local, final Type type) {
+ }
+
+ private void setFrameLocal(final int local, final Object type) {
+ int l = newLocals.length;
+ if (local >= l) {
+ Object[] a = new Object[Math.max(2 * l, local + 1)];
+ System.arraycopy(newLocals, 0, a, 0, l);
+ newLocals = a;
+ }
+ newLocals[local] = type;
+ }
+
+ private int remap(final int var, final Type type) {
+ if (var < firstLocal) {
+ return var;
+ }
+ int key = 2 * var + type.getSize() - 1;
+ int size = mapping.length;
+ if (key >= size) {
+ int[] newMapping = new int[Math.max(2 * size, key + 1)];
+ System.arraycopy(mapping, 0, newMapping, 0, size);
+ mapping = newMapping;
+ }
+ int value = mapping[key];
+ if (value == 0) {
+ value = nextLocal + 1;
+ mapping[key] = value;
+ setLocalType(nextLocal, type);
+ nextLocal += type.getSize();
+ }
+ if (value - 1 != var) {
+ changed = true;
+ }
+ return value - 1;
+ }
+
+ private int remap(final int var, final int size) {
+ if (var < firstLocal || !changed) {
+ return var;
+ }
+ int key = 2 * var + size - 1;
+ int value = key < mapping.length ? mapping[key] : 0;
+ if (value == 0) {
+ throw new IllegalStateException("Unknown local variable " + var);
+ }
+ return value - 1;
+ }
+}
diff --git a/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/Method.java b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/Method.java
new file mode 100644
index 0000000..e177d90
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/Method.java
@@ -0,0 +1,254 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.objectweb.asm.Type;
+
+/**
+ * A named method descriptor.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class Method {
+
+ /**
+ * The method name.
+ */
+ private final String name;
+
+ /**
+ * The method descriptor.
+ */
+ private final String desc;
+
+ /**
+ * Maps primitive Java type names to their descriptors.
+ */
+ private final static Map DESCRIPTORS;
+
+ static {
+ DESCRIPTORS = new HashMap();
+ DESCRIPTORS.put("void", "V");
+ DESCRIPTORS.put("byte", "B");
+ DESCRIPTORS.put("char", "C");
+ DESCRIPTORS.put("double", "D");
+ DESCRIPTORS.put("float", "F");
+ DESCRIPTORS.put("int", "I");
+ DESCRIPTORS.put("long", "J");
+ DESCRIPTORS.put("short", "S");
+ DESCRIPTORS.put("boolean", "Z");
+ }
+
+ /**
+ * Creates a new {@link Method}.
+ *
+ * @param name the method's name.
+ * @param desc the method's descriptor.
+ */
+ public Method(final String name, final String desc) {
+ this.name = name;
+ this.desc = desc;
+ }
+
+ /**
+ * Creates a new {@link Method}.
+ *
+ * @param name the method's name.
+ * @param returnType the method's return type.
+ * @param argumentTypes the method's argument types.
+ */
+ public Method(
+ final String name,
+ final Type returnType,
+ final Type[] argumentTypes)
+ {
+ this(name, Type.getMethodDescriptor(returnType, argumentTypes));
+ }
+
+ /**
+ * Returns a {@link Method} corresponding to the given Java method
+ * declaration.
+ *
+ * @param method a Java method declaration, without argument names, of the
+ * form "returnType name (argumentType1, ... argumentTypeN)", where
+ * the types are in plain Java (e.g. "int", "float",
+ * "java.util.List", ...). Classes of the java.lang package can be
+ * specified by their unqualified name; all other classes names must
+ * be fully qualified.
+ * @return a {@link Method} corresponding to the given Java method
+ * declaration.
+ * @throws IllegalArgumentException if <code>method</code> could not get
+ * parsed.
+ */
+ public static Method getMethod(final String method)
+ throws IllegalArgumentException
+ {
+ return getMethod(method, false);
+ }
+
+ /**
+ * Returns a {@link Method} corresponding to the given Java method
+ * declaration.
+ *
+ * @param method a Java method declaration, without argument names, of the
+ * form "returnType name (argumentType1, ... argumentTypeN)", where
+ * the types are in plain Java (e.g. "int", "float",
+ * "java.util.List", ...). Classes of the java.lang package may be
+ * specified by their unqualified name, depending on the
+ * defaultPackage argument; all other classes names must be fully
+ * qualified.
+ * @param defaultPackage true if unqualified class names belong to the
+ * default package, or false if they correspond to java.lang classes.
+ * For instance "Object" means "Object" if this option is true, or
+ * "java.lang.Object" otherwise.
+ * @return a {@link Method} corresponding to the given Java method
+ * declaration.
+ * @throws IllegalArgumentException if <code>method</code> could not get
+ * parsed.
+ */
+ public static Method getMethod(
+ final String method,
+ final boolean defaultPackage) throws IllegalArgumentException
+ {
+ int space = method.indexOf(' ');
+ int start = method.indexOf('(', space) + 1;
+ int end = method.indexOf(')', start);
+ if (space == -1 || start == -1 || end == -1) {
+ throw new IllegalArgumentException();
+ }
+ // TODO: Check validity of returnType, methodName and arguments.
+ String returnType = method.substring(0, space);
+ String methodName = method.substring(space + 1, start - 1).trim();
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ int p;
+ do {
+ String s;
+ p = method.indexOf(',', start);
+ if (p == -1) {
+ s = map(method.substring(start, end).trim(), defaultPackage);
+ } else {
+ s = map(method.substring(start, p).trim(), defaultPackage);
+ start = p + 1;
+ }
+ sb.append(s);
+ } while (p != -1);
+ sb.append(')');
+ sb.append(map(returnType, defaultPackage));
+ return new Method(methodName, sb.toString());
+ }
+
+ private static String map(final String type, final boolean defaultPackage) {
+ if (type.equals("")) {
+ return type;
+ }
+
+ StringBuffer sb = new StringBuffer();
+ int index = 0;
+ while ((index = type.indexOf("[]", index) + 1) > 0) {
+ sb.append('[');
+ }
+
+ String t = type.substring(0, type.length() - sb.length() * 2);
+ String desc = (String) DESCRIPTORS.get(t);
+ if (desc != null) {
+ sb.append(desc);
+ } else {
+ sb.append('L');
+ if (t.indexOf('.') < 0) {
+ if (!defaultPackage) {
+ sb.append("java/lang/");
+ }
+ sb.append(t);
+ } else {
+ sb.append(t.replace('.', '/'));
+ }
+ sb.append(';');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the name of the method described by this object.
+ *
+ * @return the name of the method described by this object.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the descriptor of the method described by this object.
+ *
+ * @return the descriptor of the method described by this object.
+ */
+ public String getDescriptor() {
+ return desc;
+ }
+
+ /**
+ * Returns the return type of the method described by this object.
+ *
+ * @return the return type of the method described by this object.
+ */
+ public Type getReturnType() {
+ return Type.getReturnType(desc);
+ }
+
+ /**
+ * Returns the argument types of the method described by this object.
+ *
+ * @return the argument types of the method described by this object.
+ */
+ public Type[] getArgumentTypes() {
+ return Type.getArgumentTypes(desc);
+ }
+
+ public String toString() {
+ return name + desc;
+ }
+
+ public boolean equals(final Object o) {
+ if (!(o instanceof Method)) {
+ return false;
+ }
+ Method other = (Method) o;
+ return name.equals(other.name) && desc.equals(other.desc);
+ }
+
+ public int hashCode() {
+ return name.hashCode() ^ desc.hashCode();
+ }
+}
\ No newline at end of file
diff --git a/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/TableSwitchGenerator.java b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/TableSwitchGenerator.java
new file mode 100644
index 0000000..54653c8
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/TableSwitchGenerator.java
@@ -0,0 +1,55 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import org.objectweb.asm.Label;
+
+/**
+ * A code generator for switch statements.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public interface TableSwitchGenerator {
+
+ /**
+ * Generates the code for a switch case.
+ *
+ * @param key the switch case key.
+ * @param end a label that corresponds to the end of the switch statement.
+ */
+ void generateCase(int key, Label end);
+
+ /**
+ * Generates the code for the default switch case.
+ */
+ void generateDefault();
+}