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