blob: 284191bde3a9046924fc62f48e5aa36c91caaf32 [file] [log] [blame]
Richard S. Hallaf656a02009-06-11 16:07:20 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package aQute.shell.runtime;
20
21import java.lang.reflect.*;
22import java.util.*;
23
24import org.osgi.service.command.*;
25
26public class Reflective {
27 public final static Object NO_MATCH = new Object();
28 public final static Set<String> KEYWORDS = new HashSet<String>(Arrays
29 .asList(new String[] {
30 "abstract", "continue", "for", "new", "switch", "assert",
31 "default", "goto", "package", "synchronized", "boolean", "do",
32 "if", "private", "this", "break", "double", "implements",
33 "protected", "throw", "byte", "else", "import", "public", "throws",
34 "case", "enum", "instanceof", "return", "transient", "catch",
35 "extends", "int", "short", "try", "char", "final", "interface",
36 "static", "void", "class", "finally", "long", "strictfp",
37 "volatile", "const", "float", "native", "super", "while" }));
38
39 public Object method(CommandSession session, Object target, String name,
40 List<Object> args) throws IllegalArgumentException,
41 IllegalAccessException, InvocationTargetException, Exception {
42 Method[] methods = target.getClass().getMethods();
43 name = name.toLowerCase();
44
45 String get = "get" + name;
46 String is = "is" + name;
47 String set = "set" + name;
48
49 if (KEYWORDS.contains(name))
50 name = "_" + name;
51
52 Method bestMethod = null;
53 Object[] bestArgs = null;
54 int match = -1;
55 for (Method m : methods) {
56 String mname = m.getName().toLowerCase();
57 if (mname.equals(name) || mname.equals(get) || mname.equals(set)
58 || mname.equals(is)) {
59 Class<?>[] types = m.getParameterTypes();
60
61 // Check if the command takes a session
62 if (types.length > 0
63 && CommandSession.class.isAssignableFrom(types[0])) {
64 args.add(0, session);
65 }
66
67 Object[] parms = new Object[types.length];
68 // if (types.length >= args.size() ) {
69 int local = coerce(session, target, types, parms, args);
70 if (local == types.length || local > match) {
71 bestMethod = m;
72 bestArgs = parms;
73 match = local;
74 }
75 // }
76 // if (match == -1 && types.length == 1
77 // && types[0] == Object[].class) {
78 // bestMethod = m;
79 // Object value = args.toArray();
80 // bestArgs = new Object[] { value };
81 // }
82 }
83 }
84
85 if (bestMethod != null) {
86 bestMethod.setAccessible(true);
87 return bestMethod.invoke(target, bestArgs);
88 } else
89 throw new IllegalArgumentException("Cannot find command:" + name
90 + " with args:" + args);
91 }
92
93 /**
94 * Complex routein to convert the arguments given from the command line to
95 * the arguments of the method call. First, an attempt is made to convert
96 * each argument. If this fails, a check is made to see if varargs can be
97 * applied. This happens when the last method argument is an array.
98 *
99 * @param session
100 * @param target
101 * @param types
102 * @param out
103 * @param in
104 * @return
105 * @throws Exception
106 */
107 private int coerce(CommandSession session, Object target, Class<?> types[],
108 Object out[], List<Object> in) throws Exception {
109 int i = 0;
110 while (i < out.length) {
111 out[i] = null;
112 try {
113 // Try to convert one argument
114 out[i] = coerce(session, target, types[i], in.get(i));
115 if (out[i] == NO_MATCH) {
116 // Failed
117 // No match, check for varargs
118 if (types[i].isArray() && i == types.length - 1) {
119 // Try to parse the remaining arguments in an array
120 Class<?> component = types[i].getComponentType();
121 Object components = Array.newInstance(component, in
122 .size()
123 - i);
124 int n = i;
125 while (i < in.size()) {
126 Object t = coerce(session, target, component, in
127 .get(i));
128 if (t == NO_MATCH)
129 return -1;
130 Array.set(components, i - n, t);
131 i++;
132 }
133 out[n] = components;
134 // Is last element, so we will quite hereafter
135 return n;
136 }
137 return -1;
138 }
139 i++;
140 } catch (Exception e) {
141 // e.printStackTrace();
142 System.err.println(e);
143 // should get rid of those exceptions, but requires
144 // reg ex matching to see if it throws an exception.
145 // dont know what is better
146 return -1;
147 }
148 }
149 return i;
150 }
151
152 Object coerce(CommandSession session, Object target, Class<?> type,
153 Object arg) throws Exception {
154 if (arg == null)
155 return null;
156
157 if (type.isAssignableFrom(arg.getClass()))
158 return arg;
159
160 Object converted = session.convert(type, arg);
161 if (converted != null)
162 return converted;
163
164 String string = arg.toString();
165 if (type.isAssignableFrom(String.class))
166 return string;
167
168 if (type.isArray()) {
169 // Must handle array types
170 return NO_MATCH;
171 } else if (!type.isPrimitive()) {
172 try {
173 return type.getConstructor(String.class).newInstance(string);
174 } catch (Exception e) {
175 return NO_MATCH;
176 }
177 }
178 if (type == boolean.class)
179 return new Boolean(string);
180 else if (type == byte.class)
181 return new Byte(string);
182 else if (type == char.class) {
183 if (string.length() == 1)
184 return string.charAt(0);
185 } else if (type == short.class)
186 return new Short(string);
187 else if (type == int.class)
188 return new Integer(string);
189 else if (type == float.class)
190 return new Float(string);
191 else if (type == double.class)
192 return new Double(string);
193 else if (type == long.class)
194 return new Long(string);
195
196 return NO_MATCH;
197 }
198
199 public static boolean hasCommand(Object target, String function) {
200 Method[] methods = target.getClass().getMethods();
201 for (Method m : methods) {
202 if (m.getName().equals(function))
203 return true;
204 }
205 return false;
206 }
207}