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; 032 033import io.ebean.enhance.asm.Type; 034 035/** 036 * A named method descriptor. 037 * 038 * @author Juozas Baliuka 039 * @author Chris Nokleberg 040 * @author Eric Bruneton 041 */ 042public class Method { 043 044 /** The method name. */ 045 private final String name; 046 047 /** The method descriptor. */ 048 private final String descriptor; 049 050 /** The descriptors of the primitive Java types (plus void). */ 051 private static final Map<String, String> PRIMITIVE_TYPE_DESCRIPTORS; 052 053 static { 054 HashMap<String, String> descriptors = new HashMap<>(); 055 descriptors.put("void", "V"); 056 descriptors.put("byte", "B"); 057 descriptors.put("char", "C"); 058 descriptors.put("double", "D"); 059 descriptors.put("float", "F"); 060 descriptors.put("int", "I"); 061 descriptors.put("long", "J"); 062 descriptors.put("short", "S"); 063 descriptors.put("boolean", "Z"); 064 PRIMITIVE_TYPE_DESCRIPTORS = descriptors; 065 } 066 067 /** 068 * Constructs a new {@link Method}. 069 * 070 * @param name the method's name. 071 * @param descriptor the method's descriptor. 072 */ 073 public Method(final String name, final String descriptor) { 074 this.name = name; 075 this.descriptor = descriptor; 076 } 077 078 /** 079 * Constructs a new {@link Method}. 080 * 081 * @param name the method's name. 082 * @param returnType the method's return type. 083 * @param argumentTypes the method's argument types. 084 */ 085 public Method(final String name, final Type returnType, final Type[] argumentTypes) { 086 this(name, Type.getMethodDescriptor(returnType, argumentTypes)); 087 } 088 089 /** 090 * Creates a new {@link Method}. 091 * 092 * @param method a java.lang.reflect method descriptor 093 * @return a {@link Method} corresponding to the given Java method declaration. 094 */ 095 public static Method getMethod(final java.lang.reflect.Method method) { 096 return new Method(method.getName(), Type.getMethodDescriptor(method)); 097 } 098 099 /** 100 * Creates a new {@link Method}. 101 * 102 * @param constructor a java.lang.reflect constructor descriptor 103 * @return a {@link Method} corresponding to the given Java constructor declaration. 104 */ 105 public static Method getMethod(final java.lang.reflect.Constructor<?> constructor) { 106 return new Method("<init>", Type.getConstructorDescriptor(constructor)); 107 } 108 109 /** 110 * Returns a {@link Method} corresponding to the given Java method declaration. 111 * 112 * @param method a Java method declaration, without argument names, of the form "returnType name 113 * (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int", 114 * "float", "java.util.List", ...). Classes of the java.lang package can be specified by their 115 * unqualified name; all other classes names must be fully qualified. 116 * @return a {@link Method} corresponding to the given Java method declaration. 117 * @throws IllegalArgumentException if <code>method</code> could not get parsed. 118 */ 119 public static Method getMethod(final String method) { 120 return getMethod(method, false); 121 } 122 123 /** 124 * Returns a {@link Method} corresponding to the given Java method declaration. 125 * 126 * @param method a Java method declaration, without argument names, of the form "returnType name 127 * (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int", 128 * "float", "java.util.List", ...). Classes of the java.lang package may be specified by their 129 * unqualified name, depending on the defaultPackage argument; all other classes names must be 130 * fully qualified. 131 * @param defaultPackage true if unqualified class names belong to the default package, or false 132 * if they correspond to java.lang classes. For instance "Object" means "Object" if this 133 * option is true, or "java.lang.Object" otherwise. 134 * @return a {@link Method} corresponding to the given Java method declaration. 135 * @throws IllegalArgumentException if <code>method</code> could not get parsed. 136 */ 137 public static Method getMethod(final String method, final boolean defaultPackage) { 138 final int spaceIndex = method.indexOf(' '); 139 int currentArgumentStartIndex = method.indexOf('(', spaceIndex) + 1; 140 final int endIndex = method.indexOf(')', currentArgumentStartIndex); 141 if (spaceIndex == -1 || currentArgumentStartIndex == 0 || endIndex == -1) { 142 throw new IllegalArgumentException(); 143 } 144 final String returnType = method.substring(0, spaceIndex); 145 final String methodName = 146 method.substring(spaceIndex + 1, currentArgumentStartIndex - 1).trim(); 147 StringBuilder stringBuilder = new StringBuilder(); 148 stringBuilder.append('('); 149 int currentArgumentEndIndex; 150 do { 151 String argumentDescriptor; 152 currentArgumentEndIndex = method.indexOf(',', currentArgumentStartIndex); 153 if (currentArgumentEndIndex == -1) { 154 argumentDescriptor = 155 getDescriptorInternal( 156 method.substring(currentArgumentStartIndex, endIndex).trim(), defaultPackage); 157 } else { 158 argumentDescriptor = 159 getDescriptorInternal( 160 method.substring(currentArgumentStartIndex, currentArgumentEndIndex).trim(), 161 defaultPackage); 162 currentArgumentStartIndex = currentArgumentEndIndex + 1; 163 } 164 stringBuilder.append(argumentDescriptor); 165 } while (currentArgumentEndIndex != -1); 166 stringBuilder.append(')').append(getDescriptorInternal(returnType, defaultPackage)); 167 return new Method(methodName, stringBuilder.toString()); 168 } 169 170 /** 171 * Returns the descriptor corresponding to the given type name. 172 * 173 * @param type a Java type name. 174 * @param defaultPackage true if unqualified class names belong to the default package, or false 175 * if they correspond to java.lang classes. For instance "Object" means "Object" if this 176 * option is true, or "java.lang.Object" otherwise. 177 * @return the descriptor corresponding to the given type name. 178 */ 179 private static String getDescriptorInternal(final String type, final boolean defaultPackage) { 180 if ("".equals(type)) { 181 return type; 182 } 183 184 StringBuilder stringBuilder = new StringBuilder(); 185 int arrayBracketsIndex = 0; 186 while ((arrayBracketsIndex = type.indexOf("[]", arrayBracketsIndex) + 1) > 0) { 187 stringBuilder.append('['); 188 } 189 190 String elementType = type.substring(0, type.length() - stringBuilder.length() * 2); 191 String descriptor = PRIMITIVE_TYPE_DESCRIPTORS.get(elementType); 192 if (descriptor != null) { 193 stringBuilder.append(descriptor); 194 } else { 195 stringBuilder.append('L'); 196 if (elementType.indexOf('.') < 0) { 197 if (!defaultPackage) { 198 stringBuilder.append("java/lang/"); 199 } 200 stringBuilder.append(elementType); 201 } else { 202 stringBuilder.append(elementType.replace('.', '/')); 203 } 204 stringBuilder.append(';'); 205 } 206 return stringBuilder.toString(); 207 } 208 209 /** 210 * Returns the name of the method described by this object. 211 * 212 * @return the name of the method described by this object. 213 */ 214 public String getName() { 215 return name; 216 } 217 218 /** 219 * Returns the descriptor of the method described by this object. 220 * 221 * @return the descriptor of the method described by this object. 222 */ 223 public String getDescriptor() { 224 return descriptor; 225 } 226 227 /** 228 * Returns the return type of the method described by this object. 229 * 230 * @return the return type of the method described by this object. 231 */ 232 public Type getReturnType() { 233 return Type.getReturnType(descriptor); 234 } 235 236 /** 237 * Returns the argument types of the method described by this object. 238 * 239 * @return the argument types of the method described by this object. 240 */ 241 public Type[] getArgumentTypes() { 242 return Type.getArgumentTypes(descriptor); 243 } 244 245 @Override 246 public String toString() { 247 return name + descriptor; 248 } 249 250 @Override 251 public boolean equals(final Object other) { 252 if (!(other instanceof Method)) { 253 return false; 254 } 255 Method otherMethod = (Method) other; 256 return name.equals(otherMethod.name) && descriptor.equals(otherMethod.descriptor); 257 } 258 259 @Override 260 public int hashCode() { 261 return name.hashCode() ^ descriptor.hashCode(); 262 } 263}