bsh.Reflect Maven / Gradle / Ivy
The newest version!
/*
* #%L
* The AIBench Shell Plugin
* %%
* Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
/*****************************************************************************
* *
* This file is part of the BeanShell Java Scripting distribution. *
* Documentation and updates may be found at http://www.beanshell.org/ *
* *
* Sun Public License Notice: *
* *
* The contents of this file are subject to the Sun Public License Version *
* 1.0 (the "License"); you may not use this file except in compliance with *
* the License. A copy of the License is available at http://www.sun.com *
* *
* The Original Code is BeanShell. The Initial Developer of the Original *
* Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
* (C) 2000. All Rights Reserved. *
* *
* GNU Public License Notice: *
* *
* Alternatively, the contents of this file may be used under the terms of *
* the GNU Lesser General Public License (the "LGPL"), in which case the *
* provisions of LGPL are applicable instead of those above. If you wish to *
* allow use of your version of this file only under the terms of the LGPL *
* and not to allow others to use your version of this file under the SPL, *
* indicate your decision by deleting the provisions above and replace *
* them with the notice and other provisions required by the LGPL. If you *
* do not delete the provisions above, a recipient may use your version of *
* this file under either the SPL or the LGPL. *
* *
* Patrick Niemeyer ([email protected]) *
* Author of Learning Java, O'Reilly & Associates *
* http://www.pat.net/~pat/ *
* *
*****************************************************************************/
package bsh;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Vector;
/**
* All of the reflection API code lies here. It is in the form of static
* utilities. Maybe this belongs in LHS.java or a generic object wrapper class.
*/
/*
* Note: This class is messy. The method and field resolution need to be
* rewritten. Various methods in here catch NoSuchMethod or NoSuchField
* exceptions during their searches. These should be rewritten to avoid having
* to catch the exceptions. Method lookups are now cached at a high level so
* they are less important, however the logic is messy.
*/
class Reflect {
/**
* Invoke method on arbitrary object instance. invocation may be static
* (through the object instance) or dynamic. Object may be a bsh scripted
* object (bsh.This type).
*
* @return the result of the method call
*/
public static Object invokeObjectMethod(
Object object, String methodName, Object[] args, Interpreter interpreter, CallStack callstack, SimpleNode callerInfo
) throws ReflectError, EvalError, InvocationTargetException {
// Bsh scripted object
if (object instanceof This && !This.isExposedThisMethod(methodName))
return ((This) object).invokeMethod(methodName, args, interpreter, callstack, callerInfo, false/* delcaredOnly */
);
// Plain Java object, find the java method
try {
BshClassManager bcm = interpreter == null ? null : interpreter.getClassManager();
Class clas = object.getClass();
Method method = resolveExpectedJavaMethod(bcm, clas, object, methodName, args, false);
return invokeMethod(method, object, args);
} catch (UtilEvalError e) {
throw e.toEvalError(callerInfo, callstack);
}
}
/**
* Invoke a method known to be static. No object instance is needed and
* there is no possibility of the method being a bsh scripted method.
*/
public static Object invokeStaticMethod(BshClassManager bcm, Class clas, String methodName, Object[] args)
throws ReflectError, UtilEvalError, InvocationTargetException {
Interpreter.debug("invoke static Method");
Method method = resolveExpectedJavaMethod(bcm, clas, null, methodName, args, true);
return invokeMethod(method, null, args);
}
/**
* Invoke the Java method on the specified object, performing needed type
* mappings on arguments and return values.
*
* @param args
* may be null
*/
static Object invokeMethod(Method method, Object object, Object[] args) throws ReflectError, InvocationTargetException {
if (args == null)
args = new Object[0];
logInvokeMethod("Invoking method (entry): ", method, args);
// Map types to assignable forms, need to keep this fast...
Object[] tmpArgs = new Object[args.length];
Class[] types = method.getParameterTypes();
try {
for (int i = 0; i < args.length; i++)
tmpArgs[i] = Types.castObject(args[i]/* rhs */, types[i]/* lhsType */, Types.ASSIGNMENT);
} catch (UtilEvalError e) {
throw new InterpreterError("illegal argument type in method invocation: " + e);
}
// unwrap any primitives
tmpArgs = Primitive.unwrap(tmpArgs);
logInvokeMethod("Invoking method (after massaging values): ", method, tmpArgs);
try {
Object returnValue = method.invoke(object, tmpArgs);
if (returnValue == null)
returnValue = Primitive.NULL;
Class returnType = method.getReturnType();
return Primitive.wrap(returnValue, returnType);
} catch (IllegalAccessException e) {
throw new ReflectError(
"Cannot access method " + StringUtil.methodString(method.getName(), method.getParameterTypes()) + " in '" + method.getDeclaringClass() + "' :"
+ e
);
}
}
public static Object getIndex(Object array, int index) throws ReflectError, UtilTargetError {
if (Interpreter.DEBUG)
Interpreter.debug("getIndex: " + array + ", index=" + index);
try {
Object val = Array.get(array, index);
return Primitive.wrap(val, array.getClass().getComponentType());
} catch (ArrayIndexOutOfBoundsException e1) {
throw new UtilTargetError(e1);
} catch (Exception e) {
throw new ReflectError("Array access:" + e);
}
}
public static void setIndex(Object array, int index, Object val) throws ReflectError, UtilTargetError {
try {
val = Primitive.unwrap(val);
Array.set(array, index, val);
} catch (ArrayStoreException e2) {
throw new UtilTargetError(e2);
} catch (IllegalArgumentException e1) {
throw new UtilTargetError(new ArrayStoreException(e1.toString()));
} catch (Exception e) {
throw new ReflectError("Array access:" + e);
}
}
public static Object getStaticFieldValue(Class clas, String fieldName) throws UtilEvalError, ReflectError {
return getFieldValue(clas, null, fieldName, true/* onlystatic */);
}
/**
*/
public static Object getObjectFieldValue(Object object, String fieldName) throws UtilEvalError, ReflectError {
if (object instanceof This)
return ((This) object).namespace.getVariable(fieldName);
else {
try {
return getFieldValue(object.getClass(), object, fieldName, false/* onlystatic */);
} catch (ReflectError e) {
// no field, try property acces
if (hasObjectPropertyGetter(object.getClass(), fieldName))
return getObjectProperty(object, fieldName);
else
throw e;
}
}
}
static LHS getLHSStaticField(Class clas, String fieldName) throws UtilEvalError, ReflectError {
Field f = resolveExpectedJavaField(clas, fieldName, true/* onlystatic */);
return new LHS(f);
}
/**
* Get an LHS reference to an object field.
*
* This method also deals with the field style property access. In the field
* does not exist we check for a property setter.
*/
static LHS getLHSObjectField(Object object, String fieldName) throws UtilEvalError, ReflectError {
if (object instanceof This) {
// I guess this is when we pass it as an argument?
// Setting locally
boolean recurse = false;
return new LHS(((This) object).namespace, fieldName, recurse);
}
try {
Field f = resolveExpectedJavaField(object.getClass(), fieldName, false/* staticOnly */);
return new LHS(object, f);
} catch (ReflectError e) {
// not a field, try property access
if (hasObjectPropertySetter(object.getClass(), fieldName))
return new LHS(object, fieldName);
else
throw e;
}
}
private static Object getFieldValue(Class clas, Object object, String fieldName, boolean staticOnly) throws UtilEvalError, ReflectError {
try {
Field f = resolveExpectedJavaField(clas, fieldName, staticOnly);
Object value = f.get(object);
Class returnType = f.getType();
return Primitive.wrap(value, returnType);
} catch (NullPointerException e) { // shouldn't happen
throw new ReflectError("???" + fieldName + " is not a static field.");
} catch (IllegalAccessException e) {
throw new ReflectError("Can't access field: " + fieldName);
}
}
/*
* Note: this method and resolveExpectedJavaField should be rewritten to
* invert this logic so that no exceptions need to be caught unecessarily.
* This is just a temporary impl. @return the field or null if not found
*/
protected static Field resolveJavaField(Class clas, String fieldName, boolean staticOnly) throws UtilEvalError {
try {
return resolveExpectedJavaField(clas, fieldName, staticOnly);
} catch (ReflectError e) {
return null;
}
}
/**
* @throws ReflectError
* if the field is not found.
*/
/*
* Note: this should really just throw NoSuchFieldException... need to
* change related signatures and code.
*/
protected static Field resolveExpectedJavaField(Class clas, String fieldName, boolean staticOnly) throws UtilEvalError, ReflectError {
Field field;
try {
if (Capabilities.haveAccessibility())
field = findAccessibleField(clas, fieldName);
else
// Class getField() finds only public (and in
// interfaces, etc.)
field = clas.getField(fieldName);
} catch (NoSuchFieldException e) {
throw new ReflectError("No such field: " + fieldName);
} catch (SecurityException e) {
throw new UtilTargetError("Security Exception while searching fields of: " + clas, e);
}
if (staticOnly && !Modifier.isStatic(field.getModifiers()))
throw new UtilEvalError("Can't reach instance field: " + fieldName + " from static context: " + clas.getName());
return field;
}
/**
* Used when accessibility capability is available to locate an occurrance
* of the field in the most derived class or superclass and set its
* accessibility flag. Note that this method is not needed in the simple non
* accessible case because we don't have to hunt for fields. Note that
* classes may declare overlapping private fields, so the distinction about
* the most derived is important. Java doesn't normally allow this kind of
* access (super won't show private variables) so there is no real syntax
* for specifying which class scope to use...
*
* @return the Field or throws NoSuchFieldException
* @throws NoSuchFieldException
* if the field is not found
*/
/*
* This method should be rewritten to use getFields() and avoid catching
* exceptions during the search.
*/
private static Field findAccessibleField(Class clas, String fieldName) throws UtilEvalError, NoSuchFieldException {
Field field;
// Quick check catches public fields include those in interfaces
try {
field = clas.getField(fieldName);
ReflectManager.RMSetAccessible(field);
return field;
} catch (NoSuchFieldException e) {}
// Now, on with the hunt...
while (clas != null) {
try {
field = clas.getDeclaredField(fieldName);
ReflectManager.RMSetAccessible(field);
return field;
// Not found, fall through to next class
} catch (NoSuchFieldException e) {}
clas = clas.getSuperclass();
}
throw new NoSuchFieldException(fieldName);
}
/**
* This method wraps resolveJavaMethod() and expects a non-null method
* result. If the method is not found it throws a descriptive ReflectError.
*/
protected static Method resolveExpectedJavaMethod(BshClassManager bcm, Class clas, Object object, String name, Object[] args, boolean staticOnly)
throws ReflectError, UtilEvalError {
if (object == Primitive.NULL)
throw new UtilTargetError(new NullPointerException("Attempt to invoke method " + name + " on null value"));
Class[] types = Types.getTypes(args);
Method method = resolveJavaMethod(bcm, clas, name, types, staticOnly);
if (method == null)
throw new ReflectError(
(staticOnly ? "Static method " : "Method ") + StringUtil.methodString(name, types) + " not found in class'" + clas.getName() + "'"
);
return method;
}
/**
* The full blown resolver method. All other method invocation methods
* delegate to this. The method may be static or dynamic unless staticOnly
* is set (in which case object may be null). If staticOnly is set then only
* static methods will be located.
*
*
* This method performs caching (caches discovered methods through the class
* manager and utilizes cached methods.)
*
*
* This method determines whether to attempt to use non-public methods based
* on Capabilities.haveAccessibility() and will set the accessibilty flag on
* the method as necessary.
*
*
* If, when directed to find a static method, this method locates a more
* specific matching instance method it will throw a descriptive exception
* analogous to the error that the Java compiler would produce. Note: as of
* 2.0.x this is a problem because there is no way to work around this with
* a cast.
*
*
* @param staticOnly
* The method located must be static, the object param may be
* null.
* @return the method or null if no matching method was found.
*/
protected static Method resolveJavaMethod(BshClassManager bcm, Class clas, String name, Class[] types, boolean staticOnly) throws UtilEvalError {
if (clas == null)
throw new InterpreterError("null class");
// Lookup previously cached method
Method method = null;
if (bcm == null)
Interpreter.debug("resolveJavaMethod UNOPTIMIZED lookup");
else
method = bcm.getResolvedMethod(clas, name, types, staticOnly);
if (method == null) {
boolean publicOnly = !Capabilities.haveAccessibility();
// Searching for the method may, itself be a priviledged
// action
try {
method = findOverloadedMethod(clas, name, types, publicOnly);
} catch (SecurityException e) {
throw new UtilTargetError("Security Exception while searching methods of: " + clas, e);
}
checkFoundStaticMethod(method, staticOnly, clas);
// This is the first time we've seen this method, set
// accessibility
// Note: even if it's a public method, we may have found
// it in a
// non-public class
if (method != null && !publicOnly) {
try {
ReflectManager.RMSetAccessible(method);
} catch (UtilEvalError e) { /* ignore */
}
}
// If succeeded cache the resolved method.
if (method != null && bcm != null)
bcm.cacheResolvedMethod(clas, types, method);
}
return method;
}
/**
* Get the candidate methods by searching the class and interface graph of
* baseClass and resolve the most specific.
*
* @return the method or null for not found
*/
private static Method findOverloadedMethod(Class baseClass, String methodName, Class[] types, boolean publicOnly) {
if (Interpreter.DEBUG)
Interpreter.debug("Searching for method: " + StringUtil.methodString(methodName, types) + " in '" + baseClass.getName() + "'");
Method[] methods = getCandidateMethods(baseClass, methodName, types.length, publicOnly);
if (Interpreter.DEBUG)
Interpreter.debug("Looking for most specific method: " + methodName);
Method method = findMostSpecificMethod(types, methods);
return method;
}
/**
* Climb the class and interface inheritence graph of the type and collect
* all methods matching the specified name and criterion. If publicOnly is
* true then only public methods in *public* classes or interfaces will be
* returned. In the normal (non-accessible) case this addresses the problem
* that arises when a package private class or private inner class
* implements a public interface or derives from a public type.
*
*
* This method primarily just delegates to gatherMethodsRecursive()
*
* @see #gatherMethodsRecursive( Class, String, int, boolean,
* java.util.Vector)
*/
static Method[] getCandidateMethods(Class baseClass, String methodName, int numArgs, boolean publicOnly) {
Vector candidates = gatherMethodsRecursive(baseClass, methodName, numArgs, publicOnly, null/* candidates */);
// return the methods in an array
Method[] ma = new Method[candidates.size()];
candidates.copyInto(ma);
return ma;
}
/**
* Accumulate all methods, optionally including non-public methods, class
* and interface, in the inheritence tree of baseClass.
*
* This method is analogous to Class getMethods() which returns all public
* methods in the inheritence tree.
*
* In the normal (non-accessible) case this also addresses the problem that
* arises when a package private class or private inner class implements a
* public interface or derives from a public type. In other words, sometimes
* we'll find public methods that we can't use directly and we have to find
* the same public method in a parent class or interface.
*
* @return the candidate methods vector
*/
private static Vector gatherMethodsRecursive(Class baseClass, String methodName, int numArgs, boolean publicOnly, Vector candidates) {
if (candidates == null)
candidates = new Vector();
// Add methods of the current class to the vector.
// In public case be careful to only add methods from a public
// class
// and to use getMethods() instead of getDeclaredMethods()
// (This addresses secure environments)
if (publicOnly) {
if (isPublic(baseClass))
addCandidates(baseClass.getMethods(), methodName, numArgs, publicOnly, candidates);
} else
addCandidates(baseClass.getDeclaredMethods(), methodName, numArgs, publicOnly, candidates);
// Does the class or interface implement interfaces?
Class[] intfs = baseClass.getInterfaces();
for (int i = 0; i < intfs.length; i++)
gatherMethodsRecursive(intfs[i], methodName, numArgs, publicOnly, candidates);
// Do we have a superclass? (interfaces don't, etc.)
Class superclass = baseClass.getSuperclass();
if (superclass != null)
gatherMethodsRecursive(superclass, methodName, numArgs, publicOnly, candidates);
return candidates;
}
private static Vector addCandidates(Method[] methods, String methodName, int numArgs, boolean publicOnly, Vector candidates) {
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName().equals(methodName) && (m.getParameterTypes().length == numArgs) && (!publicOnly || isPublic(m)))
candidates.add(m);
}
return candidates;
}
/**
* Primary object constructor This method is simpler than those that must
* resolve general method invocation because constructors are not inherited.
*
* This method determines whether to attempt to use non-public constructors
* based on Capabilities.haveAccessibility() and will set the accessibilty
* flag on the method as necessary.
*
*/
static Object constructObject(Class clas, Object[] args) throws ReflectError, InvocationTargetException {
if (clas.isInterface())
throw new ReflectError("Can't create instance of an interface: " + clas);
Object obj = null;
Class[] types = Types.getTypes(args);
Constructor con = null;
// Find the constructor.
// (there are no inherited constructors to worry about)
Constructor[] constructors = Capabilities.haveAccessibility() ? clas.getDeclaredConstructors() : clas.getConstructors();
if (Interpreter.DEBUG)
Interpreter.debug("Looking for most specific constructor: " + clas);
con = findMostSpecificConstructor(types, constructors);
if (con == null)
throw cantFindConstructor(clas, types);
if (!isPublic(con))
try {
ReflectManager.RMSetAccessible(con);
} catch (UtilEvalError e) { /* ignore */
}
args = Primitive.unwrap(args);
try {
obj = con.newInstance(args);
} catch (InstantiationException e) {
throw new ReflectError("The class " + clas + " is abstract ");
} catch (IllegalAccessException e) {
throw new ReflectError("We don't have permission to create an instance." + "Use setAccessibility(true) to enable access.");
} catch (IllegalArgumentException e) {
throw new ReflectError("The number of arguments was wrong");
}
if (obj == null)
throw new ReflectError("Couldn't construct the object");
return obj;
}
/*
* This method should parallel findMostSpecificMethod() The only reason it
* can't be combined is that Method and Constructor don't have a common
* interface for their signatures
*/
static Constructor findMostSpecificConstructor(Class[] idealMatch, Constructor[] constructors) {
int match = findMostSpecificConstructorIndex(idealMatch, constructors);
return (match == -1) ? null : constructors[match];
}
static int findMostSpecificConstructorIndex(Class[] idealMatch, Constructor[] constructors) {
Class[][] candidates = new Class[constructors.length][];
for (int i = 0; i < candidates.length; i++)
candidates[i] = constructors[i].getParameterTypes();
return findMostSpecificSignature(idealMatch, candidates);
}
/**
* Find the best match for signature idealMatch. It is assumed that the
* methods array holds only valid candidates (e.g. method name and number of
* args already matched). This method currently does not take into account
* Java 5 covariant return types... which I think will require that we find
* the most derived return type of otherwise identical best matches.
*
* @see #findMostSpecificSignature(Class[], Class[][])
* @param methods
* the set of candidate method which differ only in the types of
* their arguments.
*/
static Method findMostSpecificMethod(Class[] idealMatch, Method[] methods) {
// copy signatures into array for findMostSpecificMethod()
Class[][] candidateSigs = new Class[methods.length][];
for (int i = 0; i < methods.length; i++)
candidateSigs[i] = methods[i].getParameterTypes();
int match = findMostSpecificSignature(idealMatch, candidateSigs);
return match == -1 ? null : methods[match];
}
/**
* Implement JLS 15.11.2 Return the index of the most specific arguments
* match or -1 if no match is found. This method is used by both methods and
* constructors (which unfortunately don't share a common interface for
* signature info).
*
* @return the index of the most specific candidate
*
*/
/*
* Note: Two methods which are equally specific should not be allowed by the
* Java compiler. In this case BeanShell currently chooses the first one it
* finds. We could add a test for this case here (I believe) by adding
* another isSignatureAssignable() in the other direction between the target
* and "best" match. If the assignment works both ways then neither is more
* specific and they are ambiguous. I'll leave this test out for now because
* I'm not sure how much another test would impact performance. Method
* selection is now cached at a high level, so a few friendly extraneous
* tests shouldn't be a problem.
*/
static int findMostSpecificSignature(Class[] idealMatch, Class[][] candidates) {
for (int round = Types.FIRST_ROUND_ASSIGNABLE; round <= Types.LAST_ROUND_ASSIGNABLE; round++) {
Class[] bestMatch = null;
int bestMatchIndex = -1;
for (int i = 0; i < candidates.length; i++) {
Class[] targetMatch = candidates[i];
// If idealMatch fits targetMatch and this is
// the first match
// or targetMatch is more specific than the best
// match, make it
// the new best match.
if (
Types.isSignatureAssignable(idealMatch, targetMatch, round)
&& ((bestMatch == null) || Types.isSignatureAssignable(targetMatch, bestMatch, Types.JAVA_BASE_ASSIGNABLE))
) {
bestMatch = targetMatch;
bestMatchIndex = i;
}
}
if (bestMatch != null)
return bestMatchIndex;
}
return -1;
}
private static String accessorName(String getorset, String propName) {
return getorset + String.valueOf(Character.toUpperCase(propName.charAt(0))) + propName.substring(1);
}
public static boolean hasObjectPropertyGetter(Class clas, String propName) {
String getterName = accessorName("get", propName);
try {
clas.getMethod(getterName, new Class[0]);
return true;
} catch (NoSuchMethodException e) { /* fall through */
}
getterName = accessorName("is", propName);
try {
Method m = clas.getMethod(getterName, new Class[0]);
return (m.getReturnType() == Boolean.TYPE);
} catch (NoSuchMethodException e) {
return false;
}
}
public static boolean hasObjectPropertySetter(Class clas, String propName) {
String setterName = accessorName("set", propName);
Method[] methods = clas.getMethods();
// we don't know the right hand side of the assignment yet.
// has at least one setter of the right name?
for (int i = 0; i < methods.length; i++)
if (methods[i].getName().equals(setterName))
return true;
return false;
}
public static Object getObjectProperty(Object obj, String propName) throws UtilEvalError, ReflectError {
Object[] args = new Object[] {};
Interpreter.debug("property access: ");
Method method = null;
Exception e1 = null, e2 = null;
try {
String accessorName = accessorName("get", propName);
method = resolveExpectedJavaMethod(null/* bcm */, obj.getClass(), obj, accessorName, args, false);
} catch (Exception e) {
e1 = e;
}
if (method == null)
try {
String accessorName = accessorName("is", propName);
method = resolveExpectedJavaMethod(null/* bcm */, obj.getClass(), obj, accessorName, args, false);
if (method.getReturnType() != Boolean.TYPE)
method = null;
} catch (Exception e) {
e2 = e;
}
if (method == null)
throw new ReflectError("Error in property getter: " + e1 + (e2 != null ? " : " + e2 : ""));
try {
return invokeMethod(method, obj, args);
} catch (InvocationTargetException e) {
throw new UtilEvalError("Property accessor threw exception: " + e.getTargetException());
}
}
public static void setObjectProperty(Object obj, String propName, Object value) throws ReflectError, UtilEvalError {
String accessorName = accessorName("set", propName);
Object[] args = new Object[] {
value
};
Interpreter.debug("property access: ");
try {
Method method = resolveExpectedJavaMethod(null/* bcm */, obj.getClass(), obj, accessorName, args, false);
invokeMethod(method, obj, args);
} catch (InvocationTargetException e) {
throw new UtilEvalError("Property accessor threw exception: " + e.getTargetException());
}
}
/**
* Return a more human readable version of the type name. Specifically,
* array types are returned with postfix "[]" dimensions. e.g. return "int
* []" for integer array instead of "class [I" as would be returned by Class
* getName() in that case.
*
* @param type
* the class which name will be normalized.
* @return the normalized name of the class.
*/
public static String normalizeClassName(Class type) {
if (!type.isArray())
return type.getName();
StringBuffer className = new StringBuffer();
try {
className.append(getArrayBaseType(type).getName() + " ");
for (int i = 0; i < getArrayDimensions(type); i++)
className.append("[]");
} catch (ReflectError e) { /* shouldn't happen */
}
return className.toString();
}
/**
* returns the dimensionality of the Class returns 0 if the Class is not an
* array class
*/
public static int getArrayDimensions(Class arrayClass) {
if (!arrayClass.isArray())
return 0;
return arrayClass.getName().lastIndexOf('[') + 1; // why
// so
// cute?
}
/**
*
* Returns the base type of an array Class. throws ReflectError if the Class
* is not an array class.
*/
public static Class getArrayBaseType(Class arrayClass) throws ReflectError {
if (!arrayClass.isArray())
throw new ReflectError("The class is not an array.");
return arrayClass.getComponentType();
}
/**
* A command may be implemented as a compiled Java class containing one or
* more static invoke() methods of the correct signature. The invoke()
* methods must accept two additional leading arguments of the interpreter
* and callstack, respectively. e.g. invoke(interpreter, callstack, ... )
* This method adds the arguments and invokes the static method, returning
* the result.
*/
public static Object invokeCompiledCommand(Class commandClass, Object[] args, Interpreter interpreter, CallStack callstack) throws UtilEvalError {
// add interpereter and namespace to args list
Object[] invokeArgs = new Object[args.length + 2];
invokeArgs[0] = interpreter;
invokeArgs[1] = callstack;
System.arraycopy(args, 0, invokeArgs, 2, args.length);
BshClassManager bcm = interpreter.getClassManager();
try {
return Reflect.invokeStaticMethod(bcm, commandClass, "invoke", invokeArgs);
} catch (InvocationTargetException e) {
throw new UtilEvalError("Error in compiled command: " + e.getTargetException());
} catch (ReflectError e) {
throw new UtilEvalError("Error invoking compiled command: " + e);
}
}
private static void logInvokeMethod(String msg, Method method, Object[] args) {
if (Interpreter.DEBUG) {
Interpreter.debug(msg + method + " with args:");
for (int i = 0; i < args.length; i++)
Interpreter.debug("args[" + i + "] = " + args[i] + " type = " + args[i].getClass());
}
}
private static void checkFoundStaticMethod(Method method, boolean staticOnly, Class clas) throws UtilEvalError {
// We're looking for a static method but found an instance
// method
if (method != null && staticOnly && !isStatic(method))
throw new UtilEvalError(
"Cannot reach instance method: " + StringUtil.methodString(method.getName(), method.getParameterTypes()) + " from static context: "
+ clas.getName()
);
}
private static ReflectError cantFindConstructor(Class clas, Class[] types) {
if (types.length == 0)
return new ReflectError("Can't find default constructor for: " + clas);
else
return new ReflectError("Can't find constructor: " + StringUtil.methodString(clas.getName(), types) + " in class: " + clas.getName());
}
private static boolean isPublic(Class c) {
return Modifier.isPublic(c.getModifiers());
}
private static boolean isPublic(Method m) {
return Modifier.isPublic(m.getModifiers());
}
private static boolean isPublic(Constructor c) {
return Modifier.isPublic(c.getModifiers());
}
private static boolean isStatic(Method m) {
return Modifier.isStatic(m.getModifiers());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy