001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.util;
030:
031: import com.caucho.vfs.Path;
032: import com.caucho.vfs.Vfs;
033:
034: import java.beans.BeanInfo;
035: import java.beans.PropertyDescriptor;
036: import java.lang.reflect.Constructor;
037: import java.lang.reflect.Method;
038: import java.lang.reflect.Modifier;
039: import java.util.HashMap;
040: import java.util.logging.Logger;
041:
042: /**
043: * Bean utilities.
044: */
045: public class BeanUtil {
046: static final Logger log = Log.open(BeanUtil.class);
047: static L10N L = new L10N(BeanUtil.class);
048:
049: /**
050: * Returns the bean property type.
051: *
052: * @param obj the bean object
053: * @param name the property name
054: */
055: public static Class getBeanPropertyClass(Object obj, String name) {
056: Method method = getBeanPropertyMethod(obj, name);
057:
058: if (method == null)
059: return null;
060:
061: Class[] paramTypes = method.getParameterTypes();
062: if (paramTypes.length == 1)
063: return paramTypes[0];
064: else
065: return null;
066: }
067:
068: /**
069: * Returns the bean property type.
070: *
071: * @param obj the bean object
072: * @param name the property name
073: */
074: public static Method getBeanPropertyMethod(Object obj, String name) {
075: name = configToBeanName(name);
076:
077: Class beanClass = obj.getClass();
078: Method method = getSetMethod(beanClass, name);
079:
080: if (method == null)
081: method = getAddMethod(beanClass, name);
082:
083: return method;
084: }
085:
086: public static void validateClass(Class cl, Class parent)
087: throws RegistryException {
088: if (parent.isAssignableFrom(cl)) {
089: } else if (parent.isInterface())
090: throw new RegistryException(L.l("{0} must implement {1}",
091: cl.getName(), parent.getName()));
092: else
093: throw new RegistryException(L.l("{0} must extend {1}", cl
094: .getName(), parent.getName()));
095:
096: if (cl.isInterface())
097: throw new RegistryException(L.l(
098: "{0} must be a concrete class.", cl.getName()));
099:
100: if (Modifier.isAbstract(cl.getModifiers()))
101: throw new RegistryException(L.l(
102: "{0} must not be abstract.", cl.getName()));
103:
104: if (!Modifier.isPublic(cl.getModifiers()))
105: throw new RegistryException(L.l("{0} must be public.", cl
106: .getName()));
107:
108: Constructor zero = null;
109: try {
110: zero = cl.getConstructor(new Class[0]);
111: } catch (Throwable e) {
112: }
113:
114: if (zero == null)
115: throw new RegistryException(L.l(
116: "{0} must have a public zero-arg constructor.", cl
117: .getName()));
118: }
119:
120: /**
121: * Returns the native path for a configured path name. The special cases
122: * $app-dir and $resin-home specify the root directory.
123: *
124: * @param pathName the configuration path name.
125: * @param varMap the map of path variables.
126: * @param pwd the default path.
127: *
128: * @return a real path corresponding to the path name
129: */
130: public static Path lookupPath(String pathName, HashMap varMap,
131: Path pwd) {
132: if (pwd == null)
133: pwd = Vfs.lookup();
134:
135: if (pathName.startsWith("$")) {
136: int p = pathName.indexOf('/');
137: String prefix;
138: String suffix;
139:
140: if (p > 0) {
141: prefix = pathName.substring(1, p);
142: suffix = pathName.substring(p + 1);
143: } else {
144: prefix = pathName.substring(1);
145: suffix = null;
146: }
147:
148: Object value = varMap != null ? varMap.get(prefix) : null;
149: if (value instanceof Path) {
150: pwd = (Path) value;
151: pathName = suffix;
152: }
153: }
154:
155: if (pathName == null)
156: return pwd;
157: else if (pathName.indexOf('$') < 0)
158: return pwd.lookup(pathName);
159:
160: CharBuffer cb = CharBuffer.allocate();
161: int head = 0;
162: int tail = 0;
163: while ((tail = pathName.indexOf('$', head)) >= 0) {
164: cb.append(pathName.substring(head, tail));
165:
166: if (tail + 1 == pathName.length()) {
167: cb.append('$');
168: continue;
169: }
170:
171: int ch = pathName.charAt(tail + 1);
172:
173: if (ch >= '0' && ch <= '9') {
174: for (head = tail + 1; head < pathName.length(); head++) {
175: ch = pathName.charAt(head);
176:
177: if (ch < '0' || ch > '9')
178: break;
179: }
180: } else {
181: for (head = tail + 1; head < pathName.length(); head++) {
182: ch = pathName.charAt(head);
183:
184: if (ch == '/' || ch == '\\' || ch == '$'
185: || ch == ' ')
186: break;
187: }
188: }
189:
190: String key = pathName.substring(tail + 1, head);
191: Object value = varMap != null ? varMap.get(key) : null;
192:
193: if (value == null)
194: value = System.getProperty(key);
195:
196: if (value != null)
197: cb.append(value);
198: else
199: cb.append(pathName.substring(tail, head));
200: }
201:
202: if (head > 0 && head < pathName.length())
203: cb.append(pathName.substring(head));
204:
205: return pwd.lookupNative(cb.close());
206: }
207:
208: /**
209: * Translates a configuration name to a bean name.
210: *
211: * <pre>
212: * foo-bar maps to fooBar
213: * </pre>
214: */
215: private static String configToBeanName(String name) {
216: CharBuffer cb = CharBuffer.allocate();
217:
218: for (int i = 0; i < name.length(); i++) {
219: char ch = name.charAt(i);
220:
221: if (ch == '-')
222: cb.append(Character.toUpperCase(name.charAt(++i)));
223: else
224: cb.append(ch);
225: }
226:
227: return cb.close();
228: }
229:
230: /**
231: * Returns an add method matching the name.
232: */
233: private static Method getAddMethod(Class cl, String name) {
234: name = "add" + name;
235:
236: Method[] methods = cl.getMethods();
237:
238: for (int i = 0; i < methods.length; i++) {
239: if (!Modifier.isPublic(methods[i].getModifiers()))
240: continue;
241:
242: if (!name.equalsIgnoreCase(methods[i].getName()))
243: continue;
244:
245: if (methods[i].getParameterTypes().length == 1)
246: return methods[i];
247: }
248:
249: return null;
250: }
251:
252: /**
253: * Returns the method matching the name.
254: */
255: static private Method getMethod(Method[] methods, String name) {
256: Method method = null;
257: for (int i = 0; i < methods.length; i++) {
258: method = methods[i];
259:
260: if (!Modifier.isPublic(method.getModifiers()))
261: continue;
262:
263: if (!Modifier.isPublic(method.getDeclaringClass()
264: .getModifiers()))
265: continue;
266:
267: if (method.getName().equals(name))
268: return method;
269: }
270:
271: return null;
272: }
273:
274: /**
275: * Returns the method matching the name.
276: */
277: static private Method getMethod(Method[] methods, String name,
278: Class[] params) {
279: Method method = null;
280:
281: loop: for (int i = 0; i < methods.length; i++) {
282: method = methods[i];
283:
284: if (!Modifier.isPublic(method.getModifiers()))
285: continue;
286:
287: if (!Modifier.isPublic(method.getDeclaringClass()
288: .getModifiers()))
289: continue;
290:
291: if (!method.getName().equals(name))
292: continue;
293:
294: Class[] actual = method.getParameterTypes();
295:
296: if (actual.length != params.length)
297: continue;
298:
299: for (int j = 0; j < actual.length; j++) {
300: if (!actual[j].isAssignableFrom(params[j]))
301: continue loop;
302: }
303:
304: return method;
305: }
306:
307: return null;
308: }
309:
310: /**
311: * Returns a set method matching the property name.
312: */
313: public static Method getSetMethod(BeanInfo info, String propertyName) {
314: PropertyDescriptor[] pds = info.getPropertyDescriptors();
315:
316: Method method = null;
317: for (int i = 0; i < pds.length; i++) {
318: if (pds[i].getName().equals(propertyName)
319: && pds[i].getWriteMethod() != null) {
320: method = pds[i].getWriteMethod();
321:
322: if (method.getParameterTypes()[0].equals(String.class))
323: return method;
324: }
325: }
326:
327: if (method != null)
328: return method;
329:
330: return getSetMethod(info.getBeanDescriptor().getBeanClass(),
331: propertyName);
332: }
333:
334: /**
335: * Returns a set method matching the property name.
336: */
337: public static Method getSetMethod(Class cl, String propertyName) {
338: Method method = getSetMethod(cl, propertyName, false);
339:
340: if (method != null)
341: return method;
342:
343: return getSetMethod(cl, propertyName, true);
344: }
345:
346: /**
347: * Returns a set method matching the property name.
348: */
349: public static Method getSetMethod(Class cl, String propertyName,
350: boolean ignoreCase) {
351: String setName = "set" + propertyNameToMethodName(propertyName);
352:
353: Method bestMethod = null;
354:
355: for (Class ptrCl = cl; ptrCl != null; ptrCl = ptrCl
356: .getSuperclass()) {
357: Method method = getSetMethod(ptrCl.getMethods(), setName,
358: ignoreCase);
359:
360: if (method != null
361: && method.getParameterTypes()[0]
362: .equals(String.class))
363: return method;
364: else if (method != null)
365: bestMethod = method;
366: }
367:
368: if (bestMethod != null)
369: return bestMethod;
370:
371: Class[] interfaces = cl.getInterfaces();
372: for (int i = 0; i < interfaces.length; i++) {
373: Method method = getSetMethod(interfaces[i].getMethods(),
374: setName, ignoreCase);
375:
376: if (method != null
377: && method.getParameterTypes()[0]
378: .equals(String.class))
379: return method;
380: else if (method != null)
381: bestMethod = method;
382: }
383:
384: if (bestMethod != null)
385: return bestMethod;
386:
387: return null;
388: }
389:
390: /**
391: * Finds the matching set method
392: *
393: * @param method the methods for the class
394: * @param setName the method name
395: */
396: private static Method getSetMethod(Method[] methods,
397: String setName, boolean ignoreCase) {
398: for (int i = 0; i < methods.length; i++) {
399: Method method = methods[i];
400:
401: // The method name must match
402: if (!ignoreCase && !method.getName().equals(setName))
403: continue;
404:
405: // The method name must match
406: if (ignoreCase
407: && !method.getName().equalsIgnoreCase(setName))
408: continue;
409:
410: // The method must be public
411: if (!Modifier.isPublic(method.getModifiers()))
412: continue;
413:
414: // It must be in a public class or interface
415: if (!Modifier.isPublic(method.getDeclaringClass()
416: .getModifiers()))
417: continue;
418:
419: // It must have a single parameter
420: if (method.getParameterTypes().length != 1)
421: continue;
422:
423: // It must return void
424: if (method.getReturnType().equals(void.class)) {
425: return method;
426: }
427: }
428:
429: return null;
430: }
431:
432: /**
433: * Returns a set method matching the property name.
434: */
435: public static Method getGetMethod(BeanInfo info, String propertyName) {
436: PropertyDescriptor[] pds = info.getPropertyDescriptors();
437:
438: for (int i = 0; i < pds.length; i++) {
439: if (pds[i].getName().equals(propertyName)
440: && pds[i].getReadMethod() != null) {
441: if (!Modifier.isPublic(pds[i].getReadMethod()
442: .getDeclaringClass().getModifiers())) {
443: try {
444: pds[i].getReadMethod().setAccessible(true);
445: } catch (Throwable e) {
446: continue;
447: }
448: }
449:
450: return pds[i].getReadMethod();
451: }
452: }
453:
454: return getGetMethod(info.getBeanDescriptor().getBeanClass(),
455: propertyName);
456: }
457:
458: /**
459: * Returns a get method matching the property name.
460: */
461: public static Method getGetMethod(Class cl, String propertyName) {
462: Method method = getGetMethod(cl, propertyName, false);
463:
464: if (method != null)
465: return method;
466:
467: return getGetMethod(cl, propertyName, true);
468: }
469:
470: /**
471: * Returns a get method matching the property name.
472: */
473: public static Method getGetMethod(Class cl, String propertyName,
474: boolean ignoreCase) {
475: String getName = "get" + propertyNameToMethodName(propertyName);
476: String isName = "is" + propertyNameToMethodName(propertyName);
477:
478: for (Class ptrCl = cl; ptrCl != null; ptrCl = ptrCl
479: .getSuperclass()) {
480: Method method = getGetMethod(ptrCl.getDeclaredMethods(),
481: getName, isName, ignoreCase);
482:
483: if (method != null)
484: return method;
485:
486: Class[] interfaces = ptrCl.getInterfaces();
487: for (int i = 0; i < interfaces.length; i++) {
488: method = getGetMethod(interfaces[i]
489: .getDeclaredMethods(), getName, isName,
490: ignoreCase);
491:
492: if (method != null)
493: return method;
494: }
495: }
496:
497: return null;
498: }
499:
500: /**
501: * Finds the matching set method
502: *
503: * @param method the methods for the class
504: * @param setName the method name
505: */
506: private static Method getGetMethod(Method[] methods,
507: String getName, String isName, boolean ignoreCase) {
508: for (int i = 0; i < methods.length; i++) {
509: Method method = methods[i];
510:
511: // The method must be public
512: if (!Modifier.isPublic(method.getModifiers()))
513: continue;
514:
515: // It must be in a public class or interface
516: if (!Modifier.isPublic(method.getDeclaringClass()
517: .getModifiers()))
518: continue;
519:
520: // It must have no parameters
521: if (method.getParameterTypes().length != 0)
522: continue;
523:
524: // It must not return void
525: if (method.getReturnType().equals(void.class))
526: continue;
527:
528: // If it matches the get name, it's the right method
529: else if (!ignoreCase
530: && methods[i].getName().equals(getName))
531: return methods[i];
532:
533: // If it matches the get name, it's the right method
534: else if (ignoreCase
535: && methods[i].getName().equalsIgnoreCase(getName))
536: return methods[i];
537:
538: // The is methods must return boolean
539: else if (!methods[i].getReturnType().equals(boolean.class))
540: continue;
541:
542: // If it matches the is name, it must return boolean
543: else if (!ignoreCase && methods[i].getName().equals(isName))
544: return methods[i];
545:
546: // If it matches the is name, it must return boolean
547: else if (ignoreCase
548: && methods[i].getName().equalsIgnoreCase(isName))
549: return methods[i];
550: }
551:
552: return null;
553: }
554:
555: /**
556: * Converts a user's property name to a bean method name.
557: *
558: * @param propertyName the user property name
559: * @return the equivalent bean method name
560: */
561: public static String propertyNameToMethodName(String propertyName) {
562: char ch = propertyName.charAt(0);
563: if (Character.isLowerCase(ch))
564: propertyName = Character.toUpperCase(ch)
565: + propertyName.substring(1);
566:
567: return propertyName;
568: }
569:
570: /**
571: * Converts a user's property name to a bean method name.
572: *
573: * @param methodName the method name
574: * @return the equivalent property name
575: */
576: public static String methodNameToPropertyName(BeanInfo info,
577: String methodName) {
578: PropertyDescriptor[] pds = info.getPropertyDescriptors();
579:
580: for (int i = 0; i < pds.length; i++) {
581: if (pds[i].getReadMethod() != null
582: && pds[i].getReadMethod().getName().equals(
583: methodName))
584: return pds[i].getName();
585: if (pds[i].getWriteMethod() != null
586: && pds[i].getWriteMethod().getName().equals(
587: methodName))
588: return pds[i].getName();
589: }
590:
591: return methodNameToPropertyName(methodName);
592: }
593:
594: /**
595: * Converts a user's property name to a bean method name.
596: *
597: * @param methodName the method name
598: * @return the equivalent property name
599: */
600: public static String methodNameToPropertyName(String methodName) {
601: if (methodName.startsWith("get"))
602: methodName = methodName.substring(3);
603: else if (methodName.startsWith("set"))
604: methodName = methodName.substring(3);
605: else if (methodName.startsWith("is"))
606: methodName = methodName.substring(2);
607:
608: if (methodName.length() == 0)
609: return null;
610:
611: char ch = methodName.charAt(0);
612: if (Character.isUpperCase(ch)
613: && (methodName.length() == 1 || !Character
614: .isUpperCase(methodName.charAt(1)))) {
615: methodName = Character.toLowerCase(ch)
616: + methodName.substring(1);
617: }
618:
619: return methodName;
620: }
621: }
|