blob: 1b90b63a2762127477b0e4d1d3b3a10264f766d7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
// DWB16: coerce() doesn't support static methods
// DWB17: coerce() doesn't support static void main(String[]) in rfc132
// DWB18: coerce() doesn't extract cause from InvocationTargetException
// DWB19: coerce() won't add empty array to satisfy Object[] argument
package aQute.shell.runtime;
import java.lang.reflect.*;
import java.util.*;
import org.osgi.service.command.*;
public class Reflective {
public final static Object NO_MATCH = new Object();
public final static Set<String> KEYWORDS = new HashSet<String>(Arrays
.asList(new String[] {
"abstract", "continue", "for", "new", "switch", "assert",
"default", "goto", "package", "synchronized", "boolean", "do",
"if", "private", "this", "break", "double", "implements",
"protected", "throw", "byte", "else", "import", "public", "throws",
"case", "enum", "instanceof", "return", "transient", "catch",
"extends", "int", "short", "try", "char", "final", "interface",
"static", "void", "class", "finally", "long", "strictfp",
"volatile", "const", "float", "native", "super", "while" }));
public Object method(CommandSession session, Object target, String name,
List<Object> args) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException, Exception {
// derek - support static methods
//Method[] methods = target.getClass().getMethods();
Class<?> tc = (target instanceof Class) ? (Class<?>)target : target.getClass();
Method[] methods = tc.getMethods();
name = name.toLowerCase();
String get = "get" + name;
String is = "is" + name;
String set = "set" + name;
if (KEYWORDS.contains(name))
name = "_" + name;
Method bestMethod = null;
Object[] bestArgs = null;
int match = -1;
ArrayList<Class<?>[]> possibleTypes = new ArrayList<Class<?>[]>(); // derek
for (Method m : methods) {
String mname = m.getName().toLowerCase();
if (mname.equals(name) || mname.equals(get) || mname.equals(set)
|| mname.equals(is) || mname.equals("_main")) { // derek - added _main
Class<?>[] types = m.getParameterTypes();
ArrayList<Object> xargs = new ArrayList<Object>(args); // derek - BUGFIX don't modify args
// Check if the command takes a session
if (types.length > 0
&& CommandSession.class.isAssignableFrom(types[0])) {
xargs.add(0, session);
}
Object[] parms = new Object[types.length];
// if (types.length >= args.size() ) {
int local = coerce(session, target, types, parms, xargs);
if ((local >= xargs.size()) && (local >= types.length)) { // derek - stop no-args
boolean exact = (local == xargs.size() && local == types.length);
if (exact || local > match) {
bestMethod = m;
bestArgs = parms;
match = local;
}
if (exact)
break;
}
else
possibleTypes.add(types); // derek
// }
// if (match == -1 && types.length == 1
// && types[0] == Object[].class) {
// bestMethod = m;
// Object value = args.toArray();
// bestArgs = new Object[] { value };
// }
}
}
if (bestMethod != null) {
bestMethod.setAccessible(true);
// derek: BUGFIX catch InvocationTargetException
// return bestMethod.invoke(target, bestArgs);
try {
return bestMethod.invoke(target, bestArgs);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Exception)
throw (Exception)cause;
throw e;
}
} else {
//throw new IllegalArgumentException("Cannot find command:" + name + " with args:" + args);
// { derek
ArrayList<String> list = new ArrayList<String>();
for (Class<?>[] types : possibleTypes) {
StringBuilder buf = new StringBuilder();
buf.append('(');
for (Class<?> type : types) {
if (buf.length() > 1)
buf.append(", ");
buf.append(type.getSimpleName());
}
buf.append(')');
list.add(buf.toString());
}
throw new IllegalArgumentException(
String.format("Cannot coerce %s%s to any of %s", name, args, list));
// } derek
}
}
/**
* Complex routein to convert the arguments given from the command line to
* the arguments of the method call. First, an attempt is made to convert
* each argument. If this fails, a check is made to see if varargs can be
* applied. This happens when the last method argument is an array.
*
* @param session
* @param target
* @param types
* @param out
* @param in
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private int coerce(CommandSession session, Object target, Class<?> types[],
Object out[], List<Object> in) throws Exception {
int i = 0;
while (i < out.length) {
out[i] = null;
try {
// Try to convert one argument
// derek: add empty array as extra argument
//out[i] = coerce(session, target, types[i], in.get(i));
if (i == in.size())
out[i] = NO_MATCH;
else
out[i] = coerce(session, target, types[i], in.get(i));
if (out[i] == NO_MATCH) {
// Failed
// No match, check for varargs
if (types[i].isArray() && i == types.length - 1) {
// derek - expand final array arg
if (i < in.size()) {
Object arg = in.get(i);
if (arg instanceof List) {
List<Object> args = (List<Object>)arg;
in = new ArrayList<Object>(in);
in.remove(i);
in.addAll(args);
}
}
// Try to parse the remaining arguments in an array
Class<?> component = types[i].getComponentType();
Object components = Array.newInstance(component, in
.size()
- i);
int n = i;
while (i < in.size()) {
Object t = coerce(session, target, component, in
.get(i));
if (t == NO_MATCH)
return -1;
Array.set(components, i - n, t);
i++;
}
out[n] = components;
// Is last element, so we will quite hereafter
// return n;
if (i == in.size())
++i;
return i; // derek - return number of args converted
}
return -1;
}
i++;
} catch (Exception e) {
System.err.println("Reflective:" + e);
e.printStackTrace();
// should get rid of those exceptions, but requires
// reg ex matching to see if it throws an exception.
// dont know what is better
return -1;
}
}
return i;
}
Object coerce(CommandSession session, Object target, Class<?> type,
Object arg) throws Exception {
if (arg == null)
return null;
if (type.isAssignableFrom(arg.getClass()))
return arg;
Object converted = session.convert(type, arg);
if (converted != null)
return converted;
String string = arg.toString();
if (type.isAssignableFrom(String.class))
return string;
if (type.isArray()) {
// Must handle array types
return NO_MATCH;
} else if (!type.isPrimitive()) {
try {
return type.getConstructor(String.class).newInstance(string);
} catch (Exception e) {
return NO_MATCH;
}
}
try {
if (type == boolean.class)
return new Boolean(string);
else if (type == byte.class)
return new Byte(string);
else if (type == char.class) {
if (string.length() == 1)
return string.charAt(0);
} else if (type == short.class)
return new Short(string);
else if (type == int.class)
return new Integer(string);
else if (type == float.class)
return new Float(string);
else if (type == double.class)
return new Double(string);
else if (type == long.class)
return new Long(string);
}
catch (NumberFormatException e) {
}
return NO_MATCH;
}
public static boolean hasCommand(Object target, String function) {
Method[] methods = target.getClass().getMethods();
for (Method m : methods) {
if (m.getName().equals(function))
return true;
}
return false;
}
}