001/***
002 * ASM: a very small and fast Java bytecode manipulation framework
003 * Copyright (c) 2000-2011 INRIA, France Telecom
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 * 1. Redistributions of source code must retain the above copyright
010 *    notice, this list of conditions and the following disclaimer.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 *    notice, this list of conditions and the following disclaimer in the
013 *    documentation and/or other materials provided with the distribution.
014 * 3. Neither the name of the copyright holders nor the names of its
015 *    contributors may be used to endorse or promote products derived from
016 *    this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028 * THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package io.ebean.enhance.asm.commons;
031
032import io.ebean.enhance.asm.Type;
033
034import java.util.HashMap;
035import java.util.Map;
036
037/**
038 * A named method descriptor.
039 * 
040 * @author Juozas Baliuka
041 * @author Chris Nokleberg
042 * @author Eric Bruneton
043 */
044public class Method {
045
046    /**
047     * The method name.
048     */
049    private final String name;
050
051    /**
052     * The method descriptor.
053     */
054    private final String desc;
055
056    /**
057     * Maps primitive Java type names to their descriptors.
058     */
059    private static final Map<String, String> DESCRIPTORS;
060
061    static {
062        DESCRIPTORS = new HashMap<String, String>();
063        DESCRIPTORS.put("void", "V");
064        DESCRIPTORS.put("byte", "B");
065        DESCRIPTORS.put("char", "C");
066        DESCRIPTORS.put("double", "D");
067        DESCRIPTORS.put("float", "F");
068        DESCRIPTORS.put("int", "I");
069        DESCRIPTORS.put("long", "J");
070        DESCRIPTORS.put("short", "S");
071        DESCRIPTORS.put("boolean", "Z");
072    }
073
074    /**
075     * Creates a new {@link Method}.
076     * 
077     * @param name
078     *            the method's name.
079     * @param desc
080     *            the method's descriptor.
081     */
082    public Method(final String name, final String desc) {
083        this.name = name;
084        this.desc = desc;
085    }
086
087    /**
088     * Creates a new {@link Method}.
089     * 
090     * @param name
091     *            the method's name.
092     * @param returnType
093     *            the method's return type.
094     * @param argumentTypes
095     *            the method's argument types.
096     */
097    public Method(final String name, final Type returnType,
098            final Type[] argumentTypes) {
099        this(name, Type.getMethodDescriptor(returnType, argumentTypes));
100    }
101
102    /**
103     * Creates a new {@link Method}.
104     * 
105     * @param m
106     *            a java.lang.reflect method descriptor
107     * @return a {@link Method} corresponding to the given Java method
108     *         declaration.
109     */
110    public static Method getMethod(java.lang.reflect.Method m) {
111        return new Method(m.getName(), Type.getMethodDescriptor(m));
112    }
113
114    /**
115     * Creates a new {@link Method}.
116     * 
117     * @param c
118     *            a java.lang.reflect constructor descriptor
119     * @return a {@link Method} corresponding to the given Java constructor
120     *         declaration.
121     */
122    public static Method getMethod(java.lang.reflect.Constructor<?> c) {
123        return new Method("<init>", Type.getConstructorDescriptor(c));
124    }
125
126    /**
127     * Returns a {@link Method} corresponding to the given Java method
128     * declaration.
129     * 
130     * @param method
131     *            a Java method declaration, without argument names, of the form
132     *            "returnType name (argumentType1, ... argumentTypeN)", where
133     *            the types are in plain Java (e.g. "int", "float",
134     *            "java.util.List", ...). Classes of the java.lang package can
135     *            be specified by their unqualified name; all other classes
136     *            names must be fully qualified.
137     * @return a {@link Method} corresponding to the given Java method
138     *         declaration.
139     * @throws IllegalArgumentException
140     *             if <code>method</code> could not get parsed.
141     */
142    public static Method getMethod(final String method)
143            throws IllegalArgumentException {
144        return getMethod(method, false);
145    }
146
147    /**
148     * Returns a {@link Method} corresponding to the given Java method
149     * declaration.
150     * 
151     * @param method
152     *            a Java method declaration, without argument names, of the form
153     *            "returnType name (argumentType1, ... argumentTypeN)", where
154     *            the types are in plain Java (e.g. "int", "float",
155     *            "java.util.List", ...). Classes of the java.lang package may
156     *            be specified by their unqualified name, depending on the
157     *            defaultPackage argument; all other classes names must be fully
158     *            qualified.
159     * @param defaultPackage
160     *            true if unqualified class names belong to the default package,
161     *            or false if they correspond to java.lang classes. For instance
162     *            "Object" means "Object" if this option is true, or
163     *            "java.lang.Object" otherwise.
164     * @return a {@link Method} corresponding to the given Java method
165     *         declaration.
166     * @throws IllegalArgumentException
167     *             if <code>method</code> could not get parsed.
168     */
169    public static Method getMethod(final String method,
170                                   final boolean defaultPackage) throws IllegalArgumentException {
171        int space = method.indexOf(' ');
172        int start = method.indexOf('(', space) + 1;
173        int end = method.indexOf(')', start);
174        if (space == -1 || start == -1 || end == -1) {
175            throw new IllegalArgumentException();
176        }
177        String returnType = method.substring(0, space);
178        String methodName = method.substring(space + 1, start - 1).trim();
179        StringBuilder sb = new StringBuilder();
180        sb.append('(');
181        int p;
182        do {
183            String s;
184            p = method.indexOf(',', start);
185            if (p == -1) {
186                s = map(method.substring(start, end).trim(), defaultPackage);
187            } else {
188                s = map(method.substring(start, p).trim(), defaultPackage);
189                start = p + 1;
190            }
191            sb.append(s);
192        } while (p != -1);
193        sb.append(')');
194        sb.append(map(returnType, defaultPackage));
195        return new Method(methodName, sb.toString());
196    }
197
198    private static String map(final String type, final boolean defaultPackage) {
199        if ("".equals(type)) {
200            return type;
201        }
202
203        StringBuilder sb = new StringBuilder();
204        int index = 0;
205        while ((index = type.indexOf("[]", index) + 1) > 0) {
206            sb.append('[');
207        }
208
209        String t = type.substring(0, type.length() - sb.length() * 2);
210        String desc = DESCRIPTORS.get(t);
211        if (desc != null) {
212            sb.append(desc);
213        } else {
214            sb.append('L');
215            if (t.indexOf('.') < 0) {
216                if (!defaultPackage) {
217                    sb.append("java/lang/");
218                }
219                sb.append(t);
220            } else {
221                sb.append(t.replace('.', '/'));
222            }
223            sb.append(';');
224        }
225        return sb.toString();
226    }
227
228    /**
229     * Returns the name of the method described by this object.
230     * 
231     * @return the name of the method described by this object.
232     */
233    public String getName() {
234        return name;
235    }
236
237    /**
238     * Returns the descriptor of the method described by this object.
239     * 
240     * @return the descriptor of the method described by this object.
241     */
242    public String getDescriptor() {
243        return desc;
244    }
245
246    /**
247     * Returns the return type of the method described by this object.
248     * 
249     * @return the return type of the method described by this object.
250     */
251    public Type getReturnType() {
252        return Type.getReturnType(desc);
253    }
254
255    /**
256     * Returns the argument types of the method described by this object.
257     * 
258     * @return the argument types of the method described by this object.
259     */
260    public Type[] getArgumentTypes() {
261        return Type.getArgumentTypes(desc);
262    }
263
264    @Override
265    public String toString() {
266        return name + desc;
267    }
268
269    @Override
270    public boolean equals(final Object o) {
271        if (!(o instanceof Method)) {
272            return false;
273        }
274        Method other = (Method) o;
275        return name.equals(other.name) && desc.equals(other.desc);
276    }
277
278    @Override
279    public int hashCode() {
280        return name.hashCode() ^ desc.hashCode();
281    }
282}