1:
31: package ;
32:
33: import ;
34: import ;
35: import ;
36: import ;
37: import ;
38: import ;
39: import ;
40: import ;
41: import ;
42:
43:
49: public final class BeanUtility
50: {
51:
55: private static class PropertySpecification
56: {
57:
58: private String raw;
59:
60: private String name;
61:
62: private String index;
63:
64:
69: private PropertySpecification (final String raw)
70: {
71: this.raw = raw;
72: this.name = getNormalizedName(raw);
73: this.index = getIndex(raw);
74: }
75:
76:
82: private String getNormalizedName (final String property)
83: {
84: final int idx = property.indexOf('[');
85: if (idx < 0)
86: {
87: return property;
88: }
89: return property.substring(0, idx);
90: }
91:
92:
98: private String getIndex (final String property)
99: {
100: final int idx = property.indexOf('[');
101: if (idx < 0)
102: {
103: return null;
104: }
105: final int end = property.indexOf(']', idx + 1);
106: if (end < 0)
107: {
108: return null;
109: }
110: return property.substring(idx + 1, end);
111: }
112:
113: public String getRaw ()
114: {
115: return raw;
116: }
117:
118: public String getName ()
119: {
120: return name;
121: }
122:
123: public String getIndex ()
124: {
125: return index;
126: }
127:
128: public String toString ()
129: {
130: final StringBuffer b = new StringBuffer("PropertySpecification={");
131: b.append("raw=");
132: b.append(raw);
133: b.append("}");
134: return b.toString();
135: }
136: }
137:
138: private BeanInfo beanInfo;
139: private Object bean;
140: private HashMap properties;
141:
142: private BeanUtility()
143: {
144: }
145:
146: public BeanUtility (final Object o)
147: throws IntrospectionException
148: {
149: bean = o;
150:
151: beanInfo = Introspector.getBeanInfo(o.getClass());
152: properties = new HashMap();
153: final PropertyDescriptor[] propertyDescriptors =
154: beanInfo.getPropertyDescriptors();
155: for (int i = 0; i < propertyDescriptors.length; i++)
156: {
157: properties.put(propertyDescriptors[i].getName(), propertyDescriptors[i]);
158: }
159: }
160:
161:
162:
163: public BeanUtility derive (final Object o)
164: {
165: if (o.getClass().equals(bean.getClass()) == false)
166: {
167: throw new IllegalArgumentException();
168: }
169: final BeanUtility bu = new BeanUtility();
170: bu.bean = o;
171: return bu;
172: }
173:
174: public PropertyDescriptor[] getPropertyInfos ()
175: {
176: return beanInfo.getPropertyDescriptors();
177: }
178:
179: public Object getProperty (final String name)
180: throws BeanException
181: {
182: return getPropertyForSpecification(new PropertySpecification(name));
183: }
184:
185: private Object getPropertyForSpecification (final PropertySpecification name)
186: throws BeanException
187: {
188: final PropertyDescriptor pd = (PropertyDescriptor) properties.get(name.getName());
189: if (pd == null)
190: {
191: throw new BeanException("No such property:" + name);
192: }
193:
194: if (pd instanceof IndexedPropertyDescriptor && name.getIndex() != null)
195: {
196: final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
197: final Method readMethod = ipd.getIndexedReadMethod();
198: if (readMethod == null)
199: {
200: throw new BeanException("Property is not readable: " + name);
201: }
202: try
203: {
204: return readMethod.invoke(bean, new Object[]{new Integer(name.getIndex())});
205: }
206: catch (Exception e)
207: {
208: throw new BeanException("InvokationError", e);
209: }
210: }
211: else
212: {
213: final Method readMethod = pd.getReadMethod();
214: if (readMethod == null)
215: {
216: throw new BeanException("Property is not readable: " + name);
217: }
218: if (name.getIndex() != null)
219: {
220:
221: try
222: {
223:
224: final Object value = readMethod.invoke(bean, null);
225:
226: if (value == null)
227: {
228: throw new IndexOutOfBoundsException("No such index, property is null");
229: }
230: if (value.getClass().isArray() == false)
231: {
232: throw new BeanException("The property contains no array.");
233: }
234: final int index = Integer.parseInt(name.getIndex());
235: return Array.get(value, index);
236: }
237: catch(BeanException be)
238: {
239: throw be;
240: }
241: catch(IndexOutOfBoundsException iob)
242: {
243: throw iob;
244: }
245: catch(Exception e)
246: {
247: throw new BeanException("Failed to read indexed property.");
248: }
249: }
250:
251: try
252: {
253: return readMethod.invoke(bean, null);
254: }
255: catch (Exception e)
256: {
257: throw new BeanException("InvokationError", e);
258: }
259: }
260: }
261:
262: public String getPropertyAsString (final String name)
263: throws BeanException
264: {
265: final PropertySpecification ps = new PropertySpecification(name);
266: final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
267: if (pd == null)
268: {
269: throw new BeanException("No such property:" + name);
270: }
271: final Object o = getPropertyForSpecification(ps);
272: if (o == null)
273: {
274: return null;
275: }
276:
277: final ValueConverter vc =
278: ConverterRegistry.getInstance().getValueConverter(o.getClass());
279: if (vc == null)
280: {
281: throw new BeanException("Unable to handle property of type " + o.getClass()
282: .getName());
283: }
284: return vc.toAttributeValue(o);
285: }
286:
287: public void setProperty (final String name, final Object o)
288: throws BeanException
289: {
290: if (name == null)
291: {
292: throw new NullPointerException("Name must not be null");
293: }
294: setProperty(new PropertySpecification(name), o);
295: }
296:
297: private void setProperty (final PropertySpecification name, final Object o)
298: throws BeanException
299: {
300: final PropertyDescriptor pd = (PropertyDescriptor) properties.get(name.getName());
301: if (pd == null)
302: {
303: throw new BeanException("No such property:" + name);
304: }
305:
306: if (pd instanceof IndexedPropertyDescriptor && name.getIndex() != null)
307: {
308: final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
309: final Method writeMethod = ipd.getIndexedWriteMethod();
310: if (writeMethod != null)
311: {
312: try
313: {
314: writeMethod.invoke(bean, new Object[]{new Integer(name.getIndex()), o});
315: }
316: catch (Exception e)
317: {
318: throw new BeanException("InvokationError", e);
319: }
320:
321: return;
322: }
323: }
324:
325: final Method writeMethod = pd.getWriteMethod();
326: if (writeMethod == null)
327: {
328: throw new BeanException("Property is not writeable: " + name);
329: }
330:
331: if (name.getIndex() != null)
332: {
333:
334: updateArrayProperty(pd, name, o);
335: }
336: else
337: {
338: try
339: {
340: writeMethod.invoke(bean, new Object[]{o});
341: }
342: catch (Exception e)
343: {
344: throw new BeanException("InvokationError", e);
345: }
346: }
347: }
348:
349: private void updateArrayProperty (final PropertyDescriptor pd,
350: final PropertySpecification name,
351: final Object o)
352: throws BeanException
353: {
354: final Method readMethod = pd.getReadMethod();
355: if (readMethod == null)
356: {
357: throw new BeanException("Property is not readable, cannot perform array update: " + name);
358: }
359: try
360: {
361:
362: final Object value = readMethod.invoke(bean, null);
363:
364: final int index = Integer.parseInt(name.getIndex());
365: final Object array = validateArray(getPropertyType(pd), value, index);
366: Array.set(array, index, o);
367:
368: final Method writeMethod = pd.getWriteMethod();
369: writeMethod.invoke(bean, new Object[]{array});
370: }
371: catch(BeanException e)
372: {
373: throw e;
374: }
375: catch(Exception e)
376: {
377: e.printStackTrace();
378: throw new BeanException("Failed to read property, cannot perform array update: " + name);
379: }
380: }
381:
382: private Object validateArray (final Class propertyType,
383: final Object o, final int size)
384: throws BeanException
385: {
386:
387: if (propertyType.isArray() == false)
388: {
389: throw new BeanException("The property's value is no array.");
390: }
391:
392: if (o == null)
393: {
394: return Array.newInstance(propertyType.getComponentType(), size + 1);
395: }
396:
397: if (o.getClass().isArray() == false)
398: {
399: throw new BeanException("The property's value is no array.");
400: }
401:
402: final int length = Array.getLength(o);
403: if (length > size)
404: {
405: return o;
406: }
407:
408: final Object retval = Array.newInstance(o.getClass().getComponentType(), size + 1);
409: System.arraycopy(o, 0, retval, 0, length);
410: return o;
411: }
412:
413: public void setPropertyAsString (final String name, final String txt)
414: throws BeanException
415: {
416: if (name == null)
417: {
418: throw new NullPointerException("Name must not be null");
419: }
420: if (txt == null)
421: {
422: throw new NullPointerException("Text must not be null");
423: }
424: final PropertySpecification ps = new PropertySpecification(name);
425: final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
426: if (pd == null)
427: {
428: throw new BeanException("No such property:" + name);
429: }
430:
431: setPropertyAsString(name, getPropertyType(pd), txt);
432: }
433:
434: public Class getPropertyType (final String name) throws BeanException
435: {
436: if (name == null)
437: {
438: throw new NullPointerException("Name must not be null");
439: }
440: final PropertySpecification ps = new PropertySpecification(name);
441: final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
442: if (pd == null)
443: {
444: throw new BeanException("No such property:" + name);
445: }
446: return getPropertyType(pd);
447: }
448:
449: public static Class getPropertyType (final PropertyDescriptor pd)
450: throws BeanException
451: {
452: final Class typeFromDescriptor = pd.getPropertyType();
453: if (typeFromDescriptor != null)
454: {
455: return typeFromDescriptor;
456: }
457: if (pd instanceof IndexedPropertyDescriptor)
458: {
459: final IndexedPropertyDescriptor idx = (IndexedPropertyDescriptor) pd;
460: return idx.getIndexedPropertyType();
461: }
462: throw new BeanException("Unable to determine the property type.");
463: }
464:
465: public void setPropertyAsString (final String name, final Class type, final String txt)
466: throws BeanException
467: {
468: if (name == null)
469: {
470: throw new NullPointerException("Name must not be null");
471: }
472: if (type == null)
473: {
474: throw new NullPointerException("Type must not be null");
475: }
476: if (txt == null)
477: {
478: throw new NullPointerException("Text must not be null");
479: }
480: final PropertySpecification ps = new PropertySpecification(name);
481: final ValueConverter vc;
482: if (ps.getIndex() != null && type.isArray())
483: {
484: vc = ConverterRegistry.getInstance().getValueConverter(type.getComponentType());
485: }
486: else
487: {
488: vc = ConverterRegistry.getInstance().getValueConverter(type);
489: }
490: if (vc == null)
491: {
492: throw new BeanException
493: ("Unable to handle '" + type + "' for property '" + name + "'");
494: }
495: final Object o = vc.toPropertyValue(txt);
496: setProperty(ps, o);
497: }
498:
499: public String[] getProperties ()
500: throws BeanException
501: {
502: final ArrayList propertyNames = new ArrayList();
503: final PropertyDescriptor[] pd = getPropertyInfos();
504: for (int i = 0; i < pd.length; i++)
505: {
506: final PropertyDescriptor property = pd[i];
507: if (property.isHidden())
508: {
509: continue;
510: }
511: if (property.getReadMethod() == null ||
512: property.getWriteMethod() == null)
513: {
514:
515:
516: continue;
517: }
518: if (getPropertyType(property).isArray())
519: {
520: final int max = findMaximumIndex(property);
521: for (int idx = 0; idx < max; idx++)
522: {
523: propertyNames.add(property.getName() + "[" + idx + "]");
524: }
525: }
526: else
527: {
528: propertyNames.add(property.getName());
529: }
530: }
531: return (String[]) propertyNames.toArray(new String[propertyNames.size()]);
532: }
533:
534: private int findMaximumIndex (final PropertyDescriptor id)
535: {
536: try
537: {
538: final Object o = getPropertyForSpecification
539: (new PropertySpecification(id.getName()));
540: return Array.getLength(o);
541: }
542: catch (Exception e)
543: {
544:
545: }
546: return 0;
547: }
548: }