001: /*
002: * Distributed as part of c3p0 v.0.9.1.2
003: *
004: * Copyright (C) 2005 Machinery For Change, Inc.
005: *
006: * Author: Steve Waldman <swaldman@mchange.com>
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Lesser General Public License version 2.1, as
010: * published by the Free Software Foundation.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this software; see the file LICENSE. If not, write to the
019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: */
022:
023: package com.mchange.v2.beans;
024:
025: import java.beans.*;
026: import java.lang.reflect.*;
027: import java.util.*;
028: import com.mchange.v2.log.*;
029:
030: import com.mchange.v2.lang.Coerce;
031:
032: public final class BeansUtils {
033: final static MLogger logger = MLog.getLogger(BeansUtils.class);
034:
035: final static Object[] EMPTY_ARGS = new Object[0];
036:
037: public static PropertyEditor findPropertyEditor(
038: PropertyDescriptor pd) {
039: PropertyEditor out = null;
040: Class editorClass = null;
041: try {
042: editorClass = pd.getPropertyEditorClass();
043: if (editorClass != null)
044: out = (PropertyEditor) editorClass.newInstance();
045: } catch (Exception e) {
046: // e.printStackTrace();
047: // System.err.println("WARNING: Bad property editor class " + editorClass.getName() +
048: // " registered for property " + pd.getName());
049: if (logger.isLoggable(MLevel.WARNING))
050: logger
051: .log(MLevel.WARNING,
052: "Bad property editor class "
053: + editorClass.getName()
054: + " registered for property "
055: + pd.getName(), e);
056: }
057:
058: if (out == null)
059: out = PropertyEditorManager
060: .findEditor(pd.getPropertyType());
061: return out;
062: }
063:
064: public static boolean equalsByAccessibleProperties(Object bean0,
065: Object bean1) throws IntrospectionException {
066: return equalsByAccessibleProperties(bean0, bean1,
067: Collections.EMPTY_SET);
068: }
069:
070: public static boolean equalsByAccessibleProperties(Object bean0,
071: Object bean1, Collection ignoreProps)
072: throws IntrospectionException {
073: Map m0 = new HashMap();
074: Map m1 = new HashMap();
075: extractAccessiblePropertiesToMap(m0, bean0, ignoreProps);
076: extractAccessiblePropertiesToMap(m1, bean1, ignoreProps);
077: //System.err.println("Map0 -> " + m0);
078: //System.err.println("Map1 -> " + m1);
079: return m0.equals(m1);
080: }
081:
082: public static void overwriteAccessibleProperties(Object sourceBean,
083: Object destBean) throws IntrospectionException {
084: overwriteAccessibleProperties(sourceBean, destBean,
085: Collections.EMPTY_SET);
086: }
087:
088: public static void overwriteAccessibleProperties(Object sourceBean,
089: Object destBean, Collection ignoreProps)
090: throws IntrospectionException {
091: try {
092: BeanInfo beanInfo = Introspector.getBeanInfo(sourceBean
093: .getClass(), Object.class); //so we don't see message about getClass()
094: PropertyDescriptor[] pds = beanInfo
095: .getPropertyDescriptors();
096: for (int i = 0, len = pds.length; i < len; ++i) {
097: PropertyDescriptor pd = pds[i];
098: if (ignoreProps.contains(pd.getName()))
099: continue;
100:
101: Method getter = pd.getReadMethod();
102: Method setter = pd.getWriteMethod();
103:
104: if (getter == null || setter == null) {
105: if (pd instanceof IndexedPropertyDescriptor) {
106: // System.err.println("WARNING: BeansUtils.overwriteAccessibleProperties() does not");
107: // System.err.println("support indexed properties that do not provide single-valued");
108: // System.err.println("array getters and setters! [The indexed methods provide no means");
109: // System.err.println("of modifying the size of the array in the destination bean if");
110: // System.err.println("it does not match the source.]");
111:
112: if (logger.isLoggable(MLevel.WARNING))
113: logger
114: .warning("BeansUtils.overwriteAccessibleProperties() does not"
115: + " support indexed properties that do not provide single-valued"
116: + " array getters and setters! [The indexed methods provide no means"
117: + " of modifying the size of the array in the destination bean if"
118: + " it does not match the source.]");
119: }
120:
121: //System.err.println("Property inaccessible for overwriting: " + pd.getName());
122: if (logger.isLoggable(MLevel.INFO))
123: logger
124: .info("Property inaccessible for overwriting: "
125: + pd.getName());
126: } else {
127: Object value = getter
128: .invoke(sourceBean, EMPTY_ARGS);
129: setter.invoke(destBean, new Object[] { value });
130: }
131: }
132: } catch (IntrospectionException e) {
133: throw e;
134: } catch (Exception e) {
135: //e.printStackTrace();
136: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
137: && logger.isLoggable(MLevel.FINE))
138: logger
139: .log(MLevel.FINE,
140: "Converting exception to throwable IntrospectionException");
141:
142: throw new IntrospectionException(e.getMessage());
143: }
144: }
145:
146: public static void overwriteAccessiblePropertiesFromMap(
147: Map sourceMap, Object destBean, boolean skip_nulls)
148: throws IntrospectionException {
149: overwriteAccessiblePropertiesFromMap(sourceMap, destBean,
150: skip_nulls, Collections.EMPTY_SET);
151: }
152:
153: public static void overwriteAccessiblePropertiesFromMap(
154: Map sourceMap, Object destBean, boolean skip_nulls,
155: Collection ignoreProps) throws IntrospectionException {
156: overwriteAccessiblePropertiesFromMap(sourceMap, destBean,
157: skip_nulls, ignoreProps, false, MLevel.WARNING,
158: MLevel.WARNING, true);
159: }
160:
161: public static void overwriteAccessiblePropertiesFromMap(
162: Map sourceMap, Object destBean, boolean skip_nulls,
163: Collection ignoreProps, boolean coerce_strings,
164: MLevel cantWriteLevel, MLevel cantCoerceLevel,
165: boolean die_on_one_prop_failure)
166: throws IntrospectionException {
167: if (cantWriteLevel == null)
168: cantWriteLevel = MLevel.WARNING;
169: if (cantCoerceLevel == null)
170: cantCoerceLevel = MLevel.WARNING;
171:
172: Set sourceMapProps = sourceMap.keySet();
173:
174: String propName = null;
175: BeanInfo beanInfo = Introspector.getBeanInfo(destBean
176: .getClass(), Object.class); //so we don't see message about getClass()
177: PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
178: //System.err.println("ignoreProps: " + ignoreProps );
179: for (int i = 0, len = pds.length; i < len; ++i) {
180: PropertyDescriptor pd = pds[i];
181: propName = pd.getName();
182:
183: if (!sourceMapProps.contains(propName))
184: continue;
185:
186: if (ignoreProps != null && ignoreProps.contains(propName)) {
187: //System.err.println("ignoring: " + propName);
188: continue;
189: }
190: //else
191: // System.err.println("not ignoring: " + propName);
192:
193: Object propVal = sourceMap.get(propName);
194: if (propVal == null) {
195: if (skip_nulls)
196: continue;
197: //do we need to worry about primitives here?
198: }
199:
200: Method setter = pd.getWriteMethod();
201: boolean rethrow = false;
202:
203: Class propType = pd.getPropertyType();
204: ;
205:
206: // try
207: // {
208:
209: if (setter == null) {
210: if (pd instanceof IndexedPropertyDescriptor) {
211: if (logger.isLoggable(MLevel.FINER))
212: logger
213: .finer("BeansUtils.overwriteAccessiblePropertiesFromMap() does not"
214: + " support indexed properties that do not provide single-valued"
215: + " array getters and setters! [The indexed methods provide no means"
216: + " of modifying the size of the array in the destination bean if"
217: + " it does not match the source.]");
218:
219: }
220:
221: if (logger.isLoggable(cantWriteLevel)) {
222: String msg = "Property inaccessible for overwriting: "
223: + propName;
224: logger.log(cantWriteLevel, msg);
225: if (die_on_one_prop_failure) {
226: rethrow = true;
227: throw new IntrospectionException(msg);
228: }
229: }
230:
231: } else {
232: if (coerce_strings
233: && propVal != null
234: && propVal.getClass() == String.class
235: && (propType = pd.getPropertyType()) != String.class
236: && Coerce.canCoerce(propType)) {
237: Object coercedPropVal;
238: try {
239: coercedPropVal = Coerce.toObject(
240: (String) propVal, propType);
241: //System.err.println(propName + "-> coercedPropVal: " + coercedPropVal);
242: setter.invoke(destBean,
243: new Object[] { coercedPropVal });
244: } catch (IllegalArgumentException e) {
245: // thrown by Coerce.toObject()
246: // recall that NumberFormatException inherits from IllegalArgumentException
247: String msg = "Failed to coerce property: "
248: + propName + " [propVal: " + propVal
249: + "; propType: " + propType + "]";
250: if (logger.isLoggable(cantCoerceLevel))
251: logger.log(cantCoerceLevel, msg, e);
252: if (die_on_one_prop_failure) {
253: rethrow = true;
254: throw new IntrospectionException(msg);
255: }
256: } catch (Exception e) {
257: String msg = "Failed to set property: "
258: + propName + " [propVal: " + propVal
259: + "; propType: " + propType + "]";
260: if (logger.isLoggable(cantWriteLevel))
261: logger.log(cantWriteLevel, msg, e);
262: if (die_on_one_prop_failure) {
263: rethrow = true;
264: throw new IntrospectionException(msg);
265: }
266: }
267: } else {
268: try {
269: //System.err.println("invoking method: " + setter);
270: setter.invoke(destBean,
271: new Object[] { propVal });
272: } catch (Exception e) {
273: String msg = "Failed to set property: "
274: + propName + " [propVal: " + propVal
275: + "; propType: " + propType + "]";
276: if (logger.isLoggable(cantWriteLevel))
277: logger.log(cantWriteLevel, msg, e);
278: if (die_on_one_prop_failure) {
279: rethrow = true;
280: throw new IntrospectionException(msg);
281: }
282: }
283: }
284: }
285: // }
286: // catch (Exception e)
287: // {
288: // if (e instanceof IntrospectionException && rethrow)
289: // throw (IntrospectionException) e;
290: // else
291: // {
292: // String msg =
293: // "An exception occurred while trying to set property '" + propName +
294: // "' to value '" + propVal + "'. ";
295: // logger.log(MLevel.WARNING, msg, e);
296: // if (die_on_one_prop_failure)
297: // {
298: // rethrow = true;
299: // throw new IntrospectionException( msg + e.toString());
300: // }
301: // }
302: // }
303: }
304: }
305:
306: public static void appendPropNamesAndValues(
307: StringBuffer appendIntoMe, Object bean,
308: Collection ignoreProps) throws IntrospectionException {
309: Map tmp = new TreeMap(String.CASE_INSENSITIVE_ORDER);
310: extractAccessiblePropertiesToMap(tmp, bean, ignoreProps);
311: boolean first = true;
312: for (Iterator ii = tmp.keySet().iterator(); ii.hasNext();) {
313: String key = (String) ii.next();
314: Object val = tmp.get(key);
315: if (first)
316: first = false;
317: else
318: appendIntoMe.append(", ");
319: appendIntoMe.append(key);
320: appendIntoMe.append(" -> ");
321: appendIntoMe.append(val);
322: }
323: }
324:
325: public static void extractAccessiblePropertiesToMap(Map fillMe,
326: Object bean) throws IntrospectionException {
327: extractAccessiblePropertiesToMap(fillMe, bean,
328: Collections.EMPTY_SET);
329: }
330:
331: public static void extractAccessiblePropertiesToMap(Map fillMe,
332: Object bean, Collection ignoreProps)
333: throws IntrospectionException {
334: String propName = null;
335: try {
336: BeanInfo bi = Introspector.getBeanInfo(bean.getClass(),
337: Object.class);
338: PropertyDescriptor[] pds = bi.getPropertyDescriptors();
339: for (int i = 0, len = pds.length; i < len; ++i) {
340: PropertyDescriptor pd = pds[i];
341: propName = pd.getName();
342: if (ignoreProps.contains(propName))
343: continue;
344:
345: Method readMethod = pd.getReadMethod();
346: Object propVal = readMethod.invoke(bean, EMPTY_ARGS);
347: fillMe.put(propName, propVal);
348: }
349: } catch (IntrospectionException e) {
350: // if (propName != null)
351: // System.err.println("Problem occurred while overwriting property: " + propName);
352: if (logger.isLoggable(MLevel.WARNING))
353: logger
354: .warning("Problem occurred while overwriting property: "
355: + propName);
356: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
357: && logger.isLoggable(MLevel.FINE))
358: logger
359: .logp(
360: MLevel.FINE,
361: BeansUtils.class.getName(),
362: "extractAccessiblePropertiesToMap( Map fillMe, Object bean, Collection ignoreProps )",
363: (propName != null ? "Problem occurred while overwriting property: "
364: + propName
365: : "")
366: + " throwing...", e);
367: throw e;
368: } catch (Exception e) {
369: //e.printStackTrace();
370: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
371: && logger.isLoggable(MLevel.FINE))
372: logger
373: .logp(
374: MLevel.FINE,
375: BeansUtils.class.getName(),
376: "extractAccessiblePropertiesToMap( Map fillMe, Object bean, Collection ignoreProps )",
377: "Caught unexpected Exception; Converting to IntrospectionException.",
378: e);
379: throw new IntrospectionException(e.toString()
380: + (propName == null ? "" : " [" + propName + ']'));
381: }
382: }
383:
384: private static void overwriteProperty(String propName,
385: Object value, Method putativeSetter, Object target)
386: throws Exception {
387: if (putativeSetter.getDeclaringClass().isAssignableFrom(
388: target.getClass()))
389: putativeSetter.invoke(target, new Object[] { value });
390: else {
391: BeanInfo beanInfo = Introspector.getBeanInfo(target
392: .getClass(), Object.class);
393: PropertyDescriptor pd = null;
394:
395: PropertyDescriptor[] pds = beanInfo
396: .getPropertyDescriptors();
397: for (int i = 0, len = pds.length; i < len; ++i)
398: if (propName.equals(pds[i].getName())) {
399: pd = pds[i];
400: break;
401: }
402:
403: Method targetSetter = pd.getWriteMethod();
404: targetSetter.invoke(target, new Object[] { value });
405: }
406: }
407:
408: public static void overwriteSpecificAccessibleProperties(
409: Object sourceBean, Object destBean, Collection props)
410: throws IntrospectionException {
411: try {
412: Set _props = new HashSet(props);
413:
414: BeanInfo beanInfo = Introspector.getBeanInfo(sourceBean
415: .getClass(), Object.class); //so we don't see message about getClass()
416: PropertyDescriptor[] pds = beanInfo
417: .getPropertyDescriptors();
418: for (int i = 0, len = pds.length; i < len; ++i) {
419: PropertyDescriptor pd = pds[i];
420: String name = pd.getName();
421: if (!_props.remove(name))
422: continue;
423:
424: Method getter = pd.getReadMethod();
425: Method setter = pd.getWriteMethod();
426:
427: if (getter == null || setter == null) {
428: if (pd instanceof IndexedPropertyDescriptor) {
429: // System.err.println("WARNING: BeansUtils.overwriteAccessibleProperties() does not");
430: // System.err.println("support indexed properties that do not provide single-valued");
431: // System.err.println("array getters and setters! [The indexed methods provide no means");
432: // System.err.println("of modifying the size of the array in the destination bean if");
433: // System.err.println("it does not match the source.]");
434:
435: if (logger.isLoggable(MLevel.WARNING))
436: logger
437: .warning("BeansUtils.overwriteAccessibleProperties() does not"
438: + " support indexed properties that do not provide single-valued"
439: + " array getters and setters! [The indexed methods provide no means"
440: + " of modifying the size of the array in the destination bean if"
441: + " it does not match the source.]");
442: }
443:
444: if (logger.isLoggable(MLevel.INFO))
445: logger
446: .info("Property inaccessible for overwriting: "
447: + pd.getName());
448: } else {
449: Object value = getter
450: .invoke(sourceBean, EMPTY_ARGS);
451: overwriteProperty(name, value, setter, destBean);
452: //setter.invoke( destBean, new Object[] { value } );
453: }
454: }
455: if (logger.isLoggable(MLevel.WARNING)) {
456: for (Iterator ii = _props.iterator(); ii.hasNext();)
457: logger.warning("failed to find expected property: "
458: + ii.next());
459: //System.err.println("failed to find expected property: " + ii.next());
460: }
461: } catch (IntrospectionException e) {
462: throw e;
463: } catch (Exception e) {
464: //e.printStackTrace();
465: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
466: && logger.isLoggable(MLevel.FINE))
467: logger
468: .logp(
469: MLevel.FINE,
470: BeansUtils.class.getName(),
471: "overwriteSpecificAccessibleProperties( Object sourceBean, Object destBean, Collection props )",
472: "Caught unexpected Exception; Converting to IntrospectionException.",
473: e);
474: throw new IntrospectionException(e.getMessage());
475: }
476: }
477:
478: public static void debugShowPropertyChange(PropertyChangeEvent evt) {
479: System.err.println("PropertyChangeEvent: [ propertyName -> "
480: + evt.getPropertyName() + ", oldValue -> "
481: + evt.getOldValue() + ", newValue -> "
482: + evt.getNewValue() + " ]");
483: }
484:
485: private BeansUtils() {
486: }
487: }
|