001: /*
002: * (C) Copyright 2003 Nabh Information Systems, Inc.
003: *
004: * All copyright notices regarding Nabh's products MUST remain
005: * intact in the scripts and in the outputted HTML.
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package com.nabhinc.util;
023:
024: import java.lang.reflect.Field;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.sql.Types;
028: import java.util.Enumeration;
029: import java.util.Hashtable;
030: import java.util.Vector;
031:
032: import javax.portlet.PortletRequest;
033:
034: import com.nabhinc.core.NullObject;
035:
036: /**
037: *
038: *
039: * @author Padmanabh Dabke
040: * (c) 2003 Nabh Information Systems, Inc. All Rights Reserved.
041: */
042: public class ReflectionUtil {
043:
044: /**
045: * Utility method used by our ad-hoc serialization of
046: * hashtable. It is hardcoded to extract a Byte, Short,
047: * Integer, Long, String, Boolean, Float, Double, and
048: * net.nabh.coil.parser.NullObject.
049: * @return Desrialized object
050: * @param ser String containing serialized object
051: * @param c Class of the serialized object
052: */
053: private static Object extractObject(String ser, Class c) {
054: if (c.equals(String.class)) {
055: return ser;
056: } else if (c.equals(Byte.class) || c.equals(byte.class)) {
057: return new Byte(ser);
058: } else if (c.equals(Short.class) || c.equals(short.class)) {
059: return new Short(ser);
060: } else if (c.equals(Integer.class) || c.equals(int.class)) {
061: return new Integer(ser);
062: } else if (c.equals(Long.class) || c.equals(long.class)) {
063: return new Long(ser);
064: } else if (c.equals(Boolean.class) || c.equals(boolean.class)) {
065: return new Boolean(ser);
066: } else if (c.equals(Float.class) || c.equals(float.class)) {
067: return new Float(ser);
068: } else if (c.equals(Double.class) || c.equals(double.class)) {
069: return new Double(ser);
070: } else if (c.equals(NullObject.class)) {
071: return NullObject.getInstance();
072: }
073:
074: return ser;
075: }
076:
077: /**
078: * Attempts to find the Method object given the target object, method
079: * name, and the parameters
080: * @return Method invocation result
081: * @param target Target object
082: * @param name Method name
083: * @exception Exception Thrown if the method cannot be found
084: */
085: @SuppressWarnings("unchecked")
086: public static Method getMethod(Object target, String name,
087: Object[] params) throws Exception {
088:
089: Vector candidates = new Vector();
090: Method[] methods = null;
091:
092: if (params == null)
093: params = new Object[0];
094: // Get all methods
095:
096: if (target instanceof java.lang.Class)
097: methods = ((Class) target).getMethods();
098: else
099: methods = target.getClass().getMethods();
100:
101: // Get all methods with the given name and the number of
102: // parameters
103:
104: for (int i = 0; i < methods.length; i++) {
105: if (methods[i].getName().equals(name)
106: && methods[i].getParameterTypes().length == params.length) {
107:
108: candidates.addElement(methods[i]);
109: }
110: }
111:
112: if (candidates.size() == 0) {
113: throw new Exception(
114: "Could not find a matching method named " + name
115: + ".");
116: } else {
117: if (candidates.size() == 1) {
118: return (Method) candidates.elementAt(0);
119: }
120: }
121:
122: // Try to match the parameter types this time, since the weak
123: // matching results in more than one method
124: candidates = new Vector();
125: for (int i = 0; i < methods.length; i++) {
126: if (methods[i].getName().equals(name)
127: && matches(methods[i].getParameterTypes(), params)) {
128:
129: candidates.addElement(methods[i]);
130: }
131: }
132:
133: if (candidates.size() == 0) {
134: throw new Exception(
135: "Could not find a matching method named " + name
136: + ".");
137: } else if (candidates.size() == 1) {
138: return (Method) candidates.elementAt(0);
139: } else {
140: // Try a stricter match
141: try {
142: Class[] types = new Class[params.length];
143: for (int i = 0; i < types.length; i++) {
144: if (params[i] == null)
145: types[i] = java.lang.Object.class;
146: else if (params[i] instanceof Boolean)
147: types[i] = Boolean.TYPE;
148: else if (params[i] instanceof Integer)
149: types[i] = Integer.TYPE;
150: else if (params[i] instanceof Long)
151: types[i] = Long.TYPE;
152: else if (params[i] instanceof Float)
153: types[i] = Float.TYPE;
154: else if (params[i] instanceof Double)
155: types[i] = Double.TYPE;
156: else if (params[i] instanceof Character)
157: types[i] = Character.TYPE;
158: else if (params[i] instanceof Byte)
159: types[i] = Byte.TYPE;
160: else
161: types[i] = params[i].getClass();
162: }
163: return target.getClass().getMethod(name, types);
164: } catch (Exception e) {
165: }
166: throw new Exception("Multiple methods match " + name + ".");
167: }
168: }
169:
170: /**
171: * Ad-hoc serialization of a hashtable as a string array. Each
172: * hashtable entry is converted into three strings: key name,
173: * stringified object, class name of the object.
174: * @param table Hashtable to be serialized
175: * @return Serialized hashtable
176: */
177: public static String[] hashtableToStrings(Hashtable table) {
178: if (table == null)
179: return null;
180: Enumeration keys = table.keys();
181: String[] ret = new String[table.size() * 3];
182: int i = 0;
183: String key = null;
184: Object value = null;
185: while (keys.hasMoreElements()) {
186: key = (String) keys.nextElement();
187: ret[i] = key;
188: value = table.get(key);
189: ret[i + 1] = value.toString();
190: ret[i + 2] = value.getClass().getName();
191: i += 3;
192: }
193: return ret;
194: }
195:
196: /**
197: * Utility method used by getMethod to find out if the supplied
198: * arguments array matches the type array
199: * @param parms A class array representing a method signature
200: * @param args Argument array
201: */
202: public static boolean matches(Class[] parms, Object[] args) {
203: boolean match = (parms.length == args.length);
204: for (int j = 0; match && j < parms.length; j++) {
205: if (parms[j].isPrimitive()) {
206: match &= ((parms[j].equals(Boolean.TYPE) && args[j] instanceof Boolean)
207: || (parms[j].equals(Character.TYPE) && args[j] instanceof Character)
208: || (parms[j].equals(Byte.TYPE) && args[j] instanceof Number)
209: || (parms[j].equals(Short.TYPE) && args[j] instanceof Number)
210: || (parms[j].equals(Integer.TYPE) && args[j] instanceof Number)
211: || (parms[j].equals(Long.TYPE) && args[j] instanceof Number)
212: || (parms[j].equals(Float.TYPE) && args[j] instanceof Number) || (parms[j]
213: .equals(Double.TYPE) && args[j] instanceof Number));
214: } else if (args[j] == null)
215: match &= true;
216: else
217: match &= parms[j].isInstance(args[j]);
218: }
219: return match;
220: }
221:
222: /**
223: * A hashtable deserialization method corresponding to
224: * hashtableToStrings.
225: * @param strings A string array containing serialized hashtable
226: * @return Deserialized hashtable
227: */
228: @SuppressWarnings("unchecked")
229: public static Hashtable stringsToHashtable(String[] strings)
230: throws ClassNotFoundException {
231: Hashtable ret = new Hashtable();
232: if (strings == null)
233: return ret;
234: for (int i = 0; i < strings.length; i += 3) {
235: ret.put(strings[i], extractObject(strings[i + 1], Class
236: .forName(strings[i + 2])));
237: }
238: return ret;
239:
240: }
241:
242: /**
243: * Attempts to look up a field or a "setXXX" method on the target object and
244: * use it to set specified property. If the "strict" flag is true and
245: * this method fails to match property name with a field/method, it throws
246: * <code>IllegalArgumentException</code>. The value
247: * is converted to an object of the appropriate class based on the field type.
248: * Currently this method does not handle array or non-primitive properties.
249: *
250: * @param target Target object
251: * @param Property name
252: * @param String specifying property value
253: * @param strict If true and a matching field/method is not found, this method
254: * throws IllegalAccessException.
255: * @throws InvocationTargetException
256: * @throws IllegalAccessException
257: */
258: public static void setProperty(Object target, String name,
259: String value, boolean strict)
260: throws IllegalAccessException, InvocationTargetException {
261: try {
262: Field f = target.getClass().getField(name);
263: setFieldValue(target, f, value);
264: return;
265: } catch (NoSuchFieldException ex) {
266: // Ignore this since we want to see if there is a set method
267: } catch (IllegalAccessException e) {
268: // Ignore this since there may be a set method for this field
269: }
270: Method m = findSetMethod(target.getClass(), name);
271: if (m == null) {
272: if (strict)
273: throw new IllegalArgumentException(
274: "No field or set method matching property "
275: + name + ".");
276: else
277: return;
278: } else {
279: setProperty(target, m, m.getParameterTypes()[0], value);
280: }
281: }
282:
283: private static void setProperty(Object target, Method m,
284: Class fieldType, String value)
285: throws IllegalAccessException, InvocationTargetException {
286:
287: if (fieldType.equals(String.class)) {
288: m.invoke(target, new Object[] { value });
289: } else if (fieldType.equals(Integer.class)
290: || fieldType.equals(Integer.TYPE)) {
291: m.invoke(target, new Object[] { new Integer(value) });
292: } else if (fieldType.equals(Long.class)
293: || fieldType.equals(Long.TYPE)) {
294: m.invoke(target, new Object[] { new Long(value) });
295: } else if (fieldType.equals(Short.class)
296: || fieldType.equals(Short.TYPE)) {
297: m.invoke(target, new Object[] { new Short(value) });
298: } else if (fieldType.equals(Boolean.class)
299: || fieldType.equals(Boolean.TYPE)) {
300: m.invoke(target, new Object[] { new Boolean(value) });
301: } else if (fieldType.equals(Double.class)
302: || fieldType.equals(Double.TYPE)) {
303: m.invoke(target, new Object[] { new Double(value) });
304: } else if (fieldType.equals(Float.class)
305: || fieldType.equals(Float.TYPE)) {
306: m.invoke(target, new Object[] { new Float(value) });
307: }
308: }
309:
310: /**
311: * Attempts to look up a field or a "setXXX" method on the target object and
312: * use it to set specified property. The value is specified as a String. This
313: * is converted to an object of the appropriate class based on the field type.
314: * Currently this method does not handle array or non-primitive properties.
315: * @throws InvocationTargetException
316: * @throws IllegalAccessException
317: * @throws IllegalArgumentException
318: */
319: public static void setObjectProperty(Object target, String name,
320: Object value, boolean strict)
321: throws IllegalArgumentException, IllegalAccessException,
322: InvocationTargetException {
323:
324: try {
325: Field f = target.getClass().getField(name);
326: f.set(target, value);
327: return;
328: } catch (IllegalAccessException ex) {
329: // Ignore since there may be a set method for this field
330: } catch (NoSuchFieldException ex) {
331: // Ignore this since we want to see if there is a set method
332: }
333: Method m = findSetMethod(target.getClass(), name);
334: if (m == null) {
335: if (strict)
336: throw new IllegalArgumentException(
337: "No field or set method matching property "
338: + name + ".");
339: else
340: return;
341: } else {
342: m.invoke(target, new Object[] { value });
343: }
344: }
345:
346: private static void setFieldValue(Object target, Field f,
347: String value) throws IllegalAccessException {
348: Class fieldType = f.getClass();
349: if (fieldType.equals(String.class)) {
350: f.set(target, value);
351: } else if (fieldType.equals(Integer.class)
352: || fieldType.equals(Integer.TYPE)) {
353: f.set(target, new Integer(value));
354: } else if (fieldType.equals(Long.class)
355: || fieldType.equals(Long.TYPE)) {
356: f.set(target, new Long(value));
357: } else if (fieldType.equals(Short.class)
358: || fieldType.equals(Short.TYPE)) {
359: f.set(target, new Short(value));
360: } else if (fieldType.equals(Boolean.class)
361: || fieldType.equals(Boolean.TYPE)) {
362: f.set(target, new Boolean(value));
363: } else if (fieldType.equals(Double.class)
364: || fieldType.equals(Double.TYPE)) {
365: f.set(target, new Double(value));
366: } else if (fieldType.equals(Float.class)
367: || fieldType.equals(Float.TYPE)) {
368: f.set(target, new Float(value));
369: }
370: }
371:
372: public static Method findSetMethod(Class cl, String name) {
373: Method[] methods = cl.getMethods();
374:
375: String firstChar = name.substring(0, 1);
376: name = name.replaceFirst(firstChar, firstChar.toUpperCase());
377: String methodName = "set" + name;
378: for (int i = 0; i < methods.length; i++) {
379: if (methods[i].getName().equals(methodName)
380: && methods[i].getParameterTypes().length == 1) {
381: return methods[i];
382: }
383: }
384: return null;
385:
386: }
387:
388: public static Method findGetMethod(Class cl, String name) {
389: Method[] methods = cl.getMethods();
390:
391: String firstChar = name.substring(0, 1);
392: name = name.replaceFirst(firstChar, firstChar.toUpperCase());
393: String methodName = "get" + name;
394: String boolMethodName = "is" + name;
395: for (int i = 0; i < methods.length; i++) {
396: if ((methods[i].getName().equals(methodName) || methods[i]
397: .getName().equals(boolMethodName))
398: && methods[i].getParameterTypes().length == 0) {
399: return methods[i];
400: }
401: }
402: return null;
403:
404: }
405:
406: public static Object getProperty(Object target, String propName)
407: throws SecurityException, IllegalAccessException,
408: IllegalAccessException, IllegalArgumentException,
409: InvocationTargetException {
410: try {
411: Field f = target.getClass().getField(propName);
412: return f.get(target);
413: } catch (NoSuchFieldException e) {
414: // Ignore
415: }
416:
417: String firstChar = propName.substring(0, 1);
418: String methodName = "get"
419: + propName.replaceFirst(firstChar, firstChar
420: .toUpperCase());
421: Method m;
422: try {
423: m = target.getClass().getMethod(methodName, new Class[0]);
424: } catch (NoSuchMethodException e) {
425: throw new IllegalArgumentException(
426: "Failed to locate object property: " + propName);
427: }
428: if (m == null)
429: throw new IllegalArgumentException(
430: "Failed to locate object property: " + propName);
431: return m.invoke(target, new Object[0]);
432:
433: }
434:
435: public static Class getPropertyType(Object target, String propName)
436: throws SecurityException, IllegalAccessException,
437: IllegalAccessException, IllegalArgumentException,
438: InvocationTargetException {
439: try {
440: Field f = target.getClass().getField(propName);
441: return f.getType();
442: } catch (NoSuchFieldException e) {
443: // Ignore
444: }
445:
446: String firstChar = propName.substring(0, 1);
447: String methodName = "get"
448: + propName.replaceFirst(firstChar, firstChar
449: .toUpperCase());
450: Method m;
451: try {
452: m = target.getClass().getMethod(methodName, new Class[0]);
453: if (m == null)
454: throw new IllegalArgumentException(
455: "Failed to locate object property: " + propName);
456: return m.getReturnType();
457: } catch (NoSuchMethodException e) {
458: throw new IllegalArgumentException(
459: "Failed to locate object property: " + propName);
460: }
461:
462: }
463:
464: public static void setProperty(Object target, Method propSetter,
465: int propertyType, PortletRequest req, String paramName)
466: throws IllegalArgumentException, IllegalAccessException,
467: InvocationTargetException {
468:
469: String paramValue = req.getParameter(paramName);
470: if (paramValue == null || paramValue.length() == 0) {
471: if (propertyType == Types.BOOLEAN) {
472: propSetter.invoke(target,
473: new Object[] { Boolean.FALSE });
474: }
475: return;
476: }
477:
478: switch (propertyType) {
479: case Types.VARCHAR:
480:
481: propSetter.invoke(target, new Object[] { paramValue });
482: return;
483: case Types.BOOLEAN:
484: propSetter.invoke(target, new Object[] { Boolean.TRUE });
485: return;
486: }
487: }
488:
489: }
|