blob: 28e9ccd4d3e036a419604fb6181f4ac994249a94 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.ipojo.manipulation;
import java.util.*;
import org.apache.felix.ipojo.manipulation.ClassChecker.AnnotationDescriptor;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.LocalVariableNode;
/**
* Method Descriptor describe a method.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class MethodDescriptor {
/**
* Method name.
*/
private final String m_name;
/**
* Returned type.
*/
private final String m_returnType;
/**
* Argument types.
*/
private final String[] m_arguments;
/**
* The descriptor of the method.
*/
private final String m_desc;
/**
* The list of {@link AnnotationDescriptor} attached to this
* method.
*/
private List<AnnotationDescriptor> m_annotations;
/**
* The association argument (number) - {@link AnnotationDescriptor}.
*/
private Map<Integer, List<AnnotationDescriptor>> m_parameterAnnotations = new HashMap<Integer, List<AnnotationDescriptor>>();
/**
* The arguments variables.
*/
private List<LocalVariableNode> m_argLocalVariables;
/**
* The stack size to keep of the arguments.
*/
private final int m_argsVarLength;
/**
* Flag indicating is the described method is static.
*/
private final boolean m_isStatic;
/**
* The local variables by index.
* This map is used to detect the argument names.
*/
private LinkedHashMap<Integer, LocalVariableNode> m_locals = new LinkedHashMap<Integer, LocalVariableNode>();
/**
* Constructor.
* @param name : name of the method.
* @param desc : descriptor of the method.
* @param isStatic : is the method static
*/
public MethodDescriptor(String name, String desc, boolean isStatic) {
m_name = name;
m_desc = desc;
m_isStatic = isStatic;
Type ret = Type.getReturnType(desc);
Type[] args = Type.getArgumentTypes(desc);
m_returnType = getType(ret);
m_arguments = new String[args.length];
int argsVarLength = args.length;
if (!m_isStatic) {
argsVarLength++;
}
for (int i = 0; i < args.length; i++) {
String type = getType(args[i]);
m_arguments[i] = type;
if ("long".equals(type) || "double".equals(type)) {
argsVarLength++;
}
}
m_argsVarLength = argsVarLength;
}
/**
* Add an annotation to the current method.
* @param ann annotation to add
*/
public void addAnnotation(AnnotationDescriptor ann) {
if (m_annotations == null) {
m_annotations = new ArrayList<AnnotationDescriptor>();
}
m_annotations.add(ann);
}
/**
* Add an annotation to the current method.
* @param ann annotation to add
*/
public void addParameterAnnotation(int id, AnnotationDescriptor ann) {
List<AnnotationDescriptor> list = m_parameterAnnotations.get(new Integer(id));
if (list == null) {
list = new ArrayList<AnnotationDescriptor>();
m_parameterAnnotations.put(new Integer(id), list);
}
list.add(ann);
}
public List<AnnotationDescriptor> getAnnotations() {
return m_annotations;
}
public Map<Integer, List<AnnotationDescriptor>> getParameterAnnotations() {
return m_parameterAnnotations;
}
public String getDescriptor() {
return m_desc;
}
/**
* 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) {
StringBuilder args = new StringBuilder("{");
StringBuilder names = new StringBuilder("{");
args.append(m_arguments[0]);
if (m_locals.containsKey(1)) {
names.append(m_locals.get(1).name); // index +1 as the 0 is this
}
for (int i = 1; i < m_arguments.length; i++) {
args.append(",").append(m_arguments[i]);
if (m_locals.containsKey(i +1)) {
names.append(",").append(m_locals.get(i +1).name);
}
}
args.append("}");
names.append("}");
method.addAttribute(new Attribute("arguments", args.toString()));
method.addAttribute(new Attribute("names", names.toString()));
}
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:
// Append brackets.
String brackets = "";
for (int i = 0; i < type.getDimensions(); i++) {
brackets += "[]";
}
Type elemType = type.getElementType();
return getType(elemType) + brackets;
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;
}
public void addLocalVariable(String name, String desc, String signature, int index) {
m_locals.put(index, new LocalVariableNode(name, desc, signature, null, null, index));
if (index >= m_argsVarLength) {
// keep only argument-related local variables definitions (others relate to code which isn't in this method)
return;
}
if (m_argLocalVariables == null) {
m_argLocalVariables = new ArrayList<LocalVariableNode>();
}
m_argLocalVariables.add(new LocalVariableNode(name, desc, signature, null, null, index));
}
public void end() {
if (m_argLocalVariables != null && m_argLocalVariables.size() > 1) {
// sort them by index, even if from experience, argument-related variables (and only those) are already sorted
Collections.sort(m_argLocalVariables, new Comparator<LocalVariableNode>(){
public int compare(LocalVariableNode o1, LocalVariableNode o2) {
return o1.index - o2.index;
}
});
}
}
public List<LocalVariableNode> getArgumentLocalVariables() {
return m_argLocalVariables;
}
public LinkedHashMap<Integer, LocalVariableNode> getLocals() {
return m_locals;
}
}