001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.util;
017:
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.Method;
020: import java.lang.reflect.Modifier;
021: import java.util.Arrays;
022:
023: import org.apache.commons.jxpath.ExpressionContext;
024: import org.apache.commons.jxpath.JXPathException;
025:
026: /**
027: * Method lookup utilities, which find static and non-static methods as well
028: * as constructors based on a name and list of parameters.
029: *
030: * @author Dmitri Plotnikov
031: * @version $Revision: 1.7 $ $Date: 2004/02/29 14:17:43 $
032: */
033: public class MethodLookupUtils {
034:
035: private static final int NO_MATCH = 0;
036: private static final int APPROXIMATE_MATCH = 1;
037: private static final int EXACT_MATCH = 2;
038: private static final Object[] EMPTY_ARRAY = new Object[0];
039:
040: public static Constructor lookupConstructor(Class targetClass,
041: Object[] parameters) {
042: boolean tryExact = true;
043: int count = parameters == null ? 0 : parameters.length;
044: Class types[] = new Class[count];
045: for (int i = 0; i < count; i++) {
046: Object param = parameters[i];
047: if (param != null) {
048: types[i] = param.getClass();
049: } else {
050: types[i] = null;
051: tryExact = false;
052: }
053: }
054:
055: Constructor constructor = null;
056:
057: if (tryExact) {
058: // First - without type conversion
059: try {
060: constructor = targetClass.getConstructor(types);
061: if (constructor != null) {
062: return constructor;
063: }
064: } catch (NoSuchMethodException ex) {
065: // Ignore
066: }
067: }
068:
069: int currentMatch = 0;
070: boolean ambiguous = false;
071:
072: // Then - with type conversion
073: Constructor[] constructors = targetClass.getConstructors();
074: for (int i = 0; i < constructors.length; i++) {
075: int match = matchParameterTypes(constructors[i]
076: .getParameterTypes(), parameters);
077: if (match != NO_MATCH) {
078: if (match > currentMatch) {
079: constructor = constructors[i];
080: currentMatch = match;
081: ambiguous = false;
082: } else if (match == currentMatch) {
083: ambiguous = true;
084: }
085: }
086: }
087: if (ambiguous) {
088: throw new JXPathException("Ambigous constructor "
089: + Arrays.asList(parameters));
090: }
091: return constructor;
092: }
093:
094: public static Method lookupStaticMethod(Class targetClass,
095: String name, Object[] parameters) {
096: boolean tryExact = true;
097: int count = parameters == null ? 0 : parameters.length;
098: Class types[] = new Class[count];
099: for (int i = 0; i < count; i++) {
100: Object param = parameters[i];
101: if (param != null) {
102: types[i] = param.getClass();
103: } else {
104: types[i] = null;
105: tryExact = false;
106: }
107: }
108:
109: Method method = null;
110:
111: if (tryExact) {
112: // First - without type conversion
113: try {
114: method = targetClass.getMethod(name, types);
115: if (method != null
116: && Modifier.isStatic(method.getModifiers())) {
117: return method;
118: }
119: } catch (NoSuchMethodException ex) {
120: // Ignore
121: }
122: }
123:
124: int currentMatch = 0;
125: boolean ambiguous = false;
126:
127: // Then - with type conversion
128: Method[] methods = targetClass.getMethods();
129: for (int i = 0; i < methods.length; i++) {
130: if (Modifier.isStatic(methods[i].getModifiers())
131: && methods[i].getName().equals(name)) {
132: int match = matchParameterTypes(methods[i]
133: .getParameterTypes(), parameters);
134: if (match != NO_MATCH) {
135: if (match > currentMatch) {
136: method = methods[i];
137: currentMatch = match;
138: ambiguous = false;
139: } else if (match == currentMatch) {
140: ambiguous = true;
141: }
142: }
143: }
144: }
145: if (ambiguous) {
146: throw new JXPathException("Ambigous method call: " + name);
147: }
148: return method;
149: }
150:
151: public static Method lookupMethod(Class targetClass, String name,
152: Object[] parameters) {
153: if (parameters == null || parameters.length < 1
154: || parameters[0] == null) {
155: return null;
156: }
157:
158: if (matchType(targetClass, parameters[0]) == NO_MATCH) {
159: return null;
160: }
161:
162: targetClass = TypeUtils.convert(parameters[0], targetClass)
163: .getClass();
164:
165: boolean tryExact = true;
166: int count = parameters.length - 1;
167: Class types[] = new Class[count];
168: Object arguments[] = new Object[count];
169: for (int i = 0; i < count; i++) {
170: Object param = parameters[i + 1];
171: arguments[i] = param;
172: if (param != null) {
173: types[i] = param.getClass();
174: } else {
175: types[i] = null;
176: tryExact = false;
177: }
178: }
179:
180: Method method = null;
181:
182: if (tryExact) {
183: // First - without type conversion
184: try {
185: method = targetClass.getMethod(name, types);
186: if (method != null
187: && !Modifier.isStatic(method.getModifiers())) {
188: return method;
189: }
190: } catch (NoSuchMethodException ex) {
191: // Ignore
192: }
193: }
194:
195: int currentMatch = 0;
196: boolean ambiguous = false;
197:
198: // Then - with type conversion
199: Method[] methods = targetClass.getMethods();
200: for (int i = 0; i < methods.length; i++) {
201: if (!Modifier.isStatic(methods[i].getModifiers())
202: && methods[i].getName().equals(name)) {
203: int match = matchParameterTypes(methods[i]
204: .getParameterTypes(), arguments);
205: if (match != NO_MATCH) {
206: if (match > currentMatch) {
207: method = methods[i];
208: currentMatch = match;
209: ambiguous = false;
210: } else if (match == currentMatch) {
211: ambiguous = true;
212: }
213: }
214: }
215: }
216: if (ambiguous) {
217: throw new JXPathException("Ambigous method call: " + name);
218: }
219: return method;
220: }
221:
222: private static int matchParameterTypes(Class types[],
223: Object parameters[]) {
224: int pi = 0;
225: if (types.length >= 1
226: && ExpressionContext.class.isAssignableFrom(types[0])) {
227: pi++;
228: }
229: int length = parameters == null ? 0 : parameters.length;
230: if (types.length != length + pi) {
231: return NO_MATCH;
232: }
233: int totalMatch = EXACT_MATCH;
234: for (int i = 0; i < length; i++) {
235: int match = matchType(types[i + pi], parameters[i]);
236: if (match == NO_MATCH) {
237: return NO_MATCH;
238: }
239: if (match < totalMatch) {
240: totalMatch = match;
241: }
242: }
243: return totalMatch;
244: }
245:
246: private static int matchType(Class expected, Object object) {
247: if (object == null) {
248: return APPROXIMATE_MATCH;
249: }
250:
251: Class actual = object.getClass();
252:
253: if (expected.equals(actual)) {
254: return EXACT_MATCH;
255: }
256: if (expected.isAssignableFrom(actual)) {
257: return EXACT_MATCH;
258: }
259:
260: if (TypeUtils.canConvert(object, expected)) {
261: return APPROXIMATE_MATCH;
262: }
263:
264: return NO_MATCH;
265: }
266: }
|