/*
* Copyright 2016, Stuart Douglas, and individual contributors as indicated
* by the @authors tag.
*
* Licensed 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.fakereplace.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class DescriptorUtils {
public static Method getMethod(String name, String methodDesc, Class actual) {
try {
return actual.getMethod(name, argumentStringToClassArray(methodDesc, actual));
} catch (Exception e) {
// this should not happen
throw new RuntimeException(e);
}
}
public static String getDescriptor(Method method) {
return "(" + classArrayToDescriptorString(method.getParameterTypes()) + ")" + classToStringRepresentation(method.getReturnType());
}
public static String getDescriptor(Constructor<?> method) {
return "(" + classArrayToDescriptorString(method.getParameterTypes()) + ")V";
}
public static int maxLocalsFromParameters(String methodDescriptor) {
String[] params = descriptorStringToParameterArray(methodDescriptor);
int ret = params.length;
for(String i : params) {
if(i.equals("J") || i.equals("D")) {
++ret;
}
}
return ret;
}
/**
* returns an array of class types based on the method parameters this allows
* getMethod to be called based on descriptor data We also pass the class
* that the method We assume the descriptor is well formed
*
*/
public static Class<?>[] argumentStringToClassArray(String methodDescriptor, Class<?> methodClass) throws ClassNotFoundException {
int i = 1; // char 0 is a '('
List<Class<?>> classes = new ArrayList<Class<?>>();
int arraystart = -1;
while (methodDescriptor.charAt(i) != ')') {
Class<?> type = null;
if (methodDescriptor.charAt(i) == '[') {
if (arraystart == -1) {
arraystart = i;
}
} else {
if (methodDescriptor.charAt(i) == 'L') {
i++;
int start = i;
while (methodDescriptor.charAt(i) != ';') {
++i;
}
if (arraystart == -1) {
String className = methodDescriptor.substring(start, i).replace('/', '.');
try {
type = methodClass.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
type = Class.forName(className);
}
} else {
// apparently the array class syntax for
// getting an array class is the somewhat
// retarded Class.forName("[Ljava.lang.String;")
// a weird mix of internal and external formats
String className = methodDescriptor.substring(arraystart, i + 1).replace("/", ".");
try {
type = methodClass.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
type = Class.forName(className);
}
}
} else {
if (arraystart == -1) {
type = primitiveType(methodDescriptor.charAt(i));
} else {
String className = methodDescriptor.substring(arraystart, i + 1);
try {
type = methodClass.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
type = Class.forName(className);
}
}
}
arraystart = -1;
classes.add(type);
}
++i;
}
Class<?>[] ret = new Class[classes.size()];
for (i = 0; i < ret.length; ++i) {
ret[i] = classes.get(i);
}
return ret;
}
/**
* returns an array of String representations of the parameter types.
* Primitives are returned as their native representations, while clases are
* returned in the form Ljava/lang/Integer
* <p>
* this is so the class I can be determined from an int for example
*
*/
public static String[] descriptorStringToParameterArray(String methodDescriptor) {
int i = 1; // char 0 is a '('
List<String> ret = new ArrayList<String>();
int arraystart = -1;
while (methodDescriptor.charAt(i) != ')') {
String type = null;
if (methodDescriptor.charAt(i) == '[') {
if (arraystart == -1) {
arraystart = i;
}
} else {
if (methodDescriptor.charAt(i) == 'L') {
int start = i;
i++;
while (methodDescriptor.charAt(i) != ';') {
++i;
}
if (arraystart == -1) {
type = methodDescriptor.substring(start, i);
} else {
// apparently the array class syntax for
// getting an array class is the somewhat
// retarded Class.forName("[Ljava.lang.String;")
// a weird mix of internal and external formats
type = methodDescriptor.substring(arraystart, i);
}
} else {
if (arraystart == -1) {
type = methodDescriptor.charAt(i) + "";
} else {
type = methodDescriptor.substring(arraystart, i + 1);
}
}
arraystart = -1;
ret.add(type);
}
++i;
}
String[] r = new String[ret.size()];
for (int j = 0; j < ret.size(); ++j) {
r[j] = ret.get(j);
}
return r;
}
public static String methodSignitureToDescriptor(Class<?> returnType, Class<?>... params) {
StringBuilder sb = new StringBuilder("(");
sb.append(classArrayToDescriptorString(params));
sb.append(")");
sb.append(classToStringRepresentation(returnType));
return sb.toString();
}
public static String getReturnType(String descriptor) {
return descriptor.substring(descriptor.lastIndexOf(')') + 1);
}
public static String getReturnTypeInJvmFormat(String descriptor) {
String rt = descriptor.substring(descriptor.lastIndexOf(')') + 1);
if (rt.charAt(0) == 'L') {
rt = rt.substring(1);
rt = rt.substring(0, rt.length() - 1);
}
return rt;
}
/**
* e.g. Ljava/lang/Object; to java/lang/Object
*
*/
public static String getTypeStringFromDescriptorFormat(String descriptor) {
descriptor = descriptor.substring(1);
descriptor = descriptor.substring(0, descriptor.length() - 1);
return descriptor;
}
public static String getArgumentString(String descriptor) {
return descriptor.substring(0, descriptor.lastIndexOf(')') + 1);
}
public static String classArrayToDescriptorString(Class<?>... params) {
if (params == null) {
return "";
}
StringBuilder ret = new StringBuilder();
for (Class<?> c : params) {
ret.append(classToStringRepresentation(c));
}
return ret.toString();
}
public static String classToStringRepresentation(Class<?> c) {
if (void.class.equals(c)) {
return "V";
} else if (byte.class.equals(c)) {
return "B";
} else if (char.class.equals(c)) {
return "C";
} else if (double.class.equals(c)) {
return "D";
} else if (float.class.equals(c)) {
return "F";
} else if (int.class.equals(c)) {
return "I";
} else if (long.class.equals(c)) {
return "J";
} else if (short.class.equals(c)) {
return "S";
} else if (boolean.class.equals(c)) {
return "Z";
} else if (c.isArray()) {
return c.getName().replace(".", "/");
} else {
return extToInt(c.getName());
}
}
static Class<?> primitiveType(char c) {
switch (c) {
case 'V':
return void.class;
case 'B':
return byte.class;
case 'C':
return char.class;
case 'D':
return double.class;
case 'F':
return float.class;
case 'I':
return int.class;
case 'J':
return long.class;
case 'S':
return short.class;
case 'Z':
return boolean.class;
}
return null;
}
public static String extToInt(String className) {
String repl = className.replace(".", "/");
return 'L' + repl + ';';
}
public static boolean isPrimitive(String descriptor) {
if (descriptor.length() == 1) {
return true;
}
return false;
}
public static boolean isWide(String descriptor) {
if (!isPrimitive(descriptor)) {
return false;
}
char c = descriptor.charAt(0);
if (c == 'D' || c == 'J') {
return true;
}
return false;
}
}