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