1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.kns.web.struts.form.pojo;
18
19 import org.apache.commons.beanutils.DynaBean;
20 import org.apache.commons.beanutils.DynaProperty;
21 import org.apache.commons.beanutils.MappedPropertyDescriptor;
22 import org.apache.commons.beanutils.MethodUtils;
23 import org.apache.commons.beanutils.NestedNullException;
24 import org.apache.commons.beanutils.PropertyUtils;
25 import org.apache.commons.beanutils.PropertyUtilsBean;
26 import org.apache.commons.beanutils.WrapDynaBean;
27 import org.apache.commons.collections.FastHashMap;
28 import org.apache.log4j.Logger;
29 import org.kuali.rice.core.web.format.Formatter;
30 import org.kuali.rice.kns.service.KNSServiceLocator;
31 import org.kuali.rice.krad.bo.PersistableBusinessObject;
32 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
33 import org.kuali.rice.krad.service.LegacyDataAdapter;
34 import org.kuali.rice.krad.service.PersistenceStructureService;
35 import org.kuali.rice.krad.util.ObjectUtils;
36
37 import java.beans.IndexedPropertyDescriptor;
38 import java.beans.IntrospectionException;
39 import java.beans.PropertyDescriptor;
40 import java.lang.reflect.InvocationTargetException;
41 import java.lang.reflect.Method;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47
48
49
50
51
52
53
54
55
56
57 @Deprecated
58 public class PojoPropertyUtilsBean extends PropertyUtilsBean {
59
60 public static final Logger LOG = Logger.getLogger(PojoPropertyUtilsBean.class.getName());
61
62
63
64
65 public static interface CollectionItemClassProvider {
66 public Class getCollectionItemClass(Object bean, String property);
67 }
68
69
70
71
72 public static class LegacyDataAdapterProvider implements CollectionItemClassProvider {
73 protected static LegacyDataAdapter legacyDataAdapter = null;
74 protected static LegacyDataAdapter getLegacyDataAdapter() {
75 if (legacyDataAdapter == null) {
76 legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter();
77 }
78 return legacyDataAdapter;
79 }
80 @Override
81 public Class getCollectionItemClass(Object bean, String property) {
82 Map<String, Class> collectionObjectTypes = getLegacyDataAdapter().listCollectionObjectTypes(bean.getClass());
83 return collectionObjectTypes.get(property);
84 }
85 }
86
87
88
89
90 public static class PersistenceStructureServiceProvider implements CollectionItemClassProvider {
91 protected static PersistenceStructureService persistenceStructureService = null;
92 protected static PersistenceStructureService getPersistenceStructureService() {
93 if (persistenceStructureService == null) {
94 persistenceStructureService = KNSServiceLocator.getPersistenceStructureService();
95 }
96 return persistenceStructureService;
97 }
98
99 @Override
100 public Class getCollectionItemClass(Object bean, String property) {
101 Map<String, Class> collectionObjectTypes = getPersistenceStructureService().listCollectionObjectTypes(bean.getClass());
102 return collectionObjectTypes.get(property);
103 }
104 }
105
106
107 protected static CollectionItemClassProvider collectionItemClassProvider = new LegacyDataAdapterProvider();
108
109
110 public PojoPropertyUtilsBean() {
111 super();
112 }
113
114
115 public Object getProperty(Object bean, String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
116
117 if (!(bean instanceof PojoForm))
118 return super.getProperty(bean, key);
119
120 PojoForm form = (PojoForm) bean;
121 Map unconvertedValues = form.getUnconvertedValues();
122
123 if (unconvertedValues.containsKey(key))
124 return unconvertedValues.get(key);
125
126 Object val = getNestedProperty(bean, key);
127 Class type = (val!=null)?val.getClass():null;
128 if ( type == null ) {
129 try {
130 type = getPropertyType(bean, key);
131 } catch ( Exception ex ) {
132 type = String.class;
133 LOG.warn( "Unable to get property type for Class: " + bean.getClass().getName() + "/Property: " + key );
134 }
135 }
136 return (Formatter.isSupportedType(type) ? form.formatValue(val, key, type) : val);
137
138 }
139
140
141 private Map<String,List<Method>> cache = new HashMap<String, List<Method>>();
142 private static Map<String,Method> readMethodCache = new HashMap<String, Method>();
143 private IntrospectionException introspectionException = new IntrospectionException( "" );
144
145 public Object fastGetNestedProperty(Object obj, String propertyName) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
146
147
148 List<Method> methods = (List<Method>) cache.get(propertyName + obj.getClass().getName());
149 if (methods == null) {
150 methods = new ArrayList<Method>();
151 Object currentObj = obj;
152 Class<?> currentObjClass = currentObj.getClass();
153
154 for (String currentPropertyName : propertyName.split("\\.") ) {
155 String cacheKey = currentObjClass.getName() + currentPropertyName;
156 Method readMethod = readMethodCache.get( cacheKey );
157 if ( readMethod == null ) {
158 synchronized (readMethodCache) {
159
160
161 if ( readMethodCache.containsKey(cacheKey) ) {
162 throw introspectionException;
163 }
164 try {
165 try {
166 readMethod = currentObjClass.getMethod("get" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
167 } catch (NoSuchMethodException e) {
168 readMethod = currentObjClass.getMethod("is" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
169 }
170 } catch ( NoSuchMethodException ex ) {
171
172 readMethodCache.put( cacheKey, null );
173 throw introspectionException;
174 }
175 readMethodCache.put(cacheKey, readMethod );
176 }
177 }
178 methods.add(readMethod);
179 currentObjClass = readMethod.getReturnType();
180 }
181 synchronized (cache) {
182 cache.put(propertyName + obj.getClass().getName(), methods);
183 }
184 }
185
186 for ( Method method : methods ) {
187 obj = method.invoke(obj, (Object[])null);
188 }
189
190
191
192 return obj;
193 }
194
195
196
197
198
199
200 @Override
201 public boolean isWriteable(Object bean, String name) {
202
203 if (bean == null) {
204 throw new IllegalArgumentException("No bean specified");
205 }
206 if (name == null) {
207 throw new IllegalArgumentException("No name specified for bean class '" +
208 bean.getClass() + "'");
209 }
210
211
212 name = getResolver().getProperty(name);
213
214
215
216 if (bean instanceof WrapDynaBean) {
217 bean = ((WrapDynaBean)bean).getInstance();
218 }
219
220
221 if (bean instanceof DynaBean) {
222
223 return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
224 } else {
225 try {
226 PropertyDescriptor desc =
227 getPropertyDescriptor(bean, name);
228 if (desc != null) {
229 Method writeMethod = desc.getWriteMethod();
230 if (writeMethod == null) {
231 if (desc instanceof IndexedPropertyDescriptor) {
232 writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
233 } else if (desc instanceof MappedPropertyDescriptor) {
234 writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
235 }
236 writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
237 }
238 return (writeMethod != null);
239 } else {
240 return (false);
241 }
242 } catch (IllegalAccessException e) {
243 return (false);
244 } catch (InvocationTargetException e) {
245 return (false);
246 } catch (NoSuchMethodException e) {
247 return (false);
248 }
249 }
250
251 }
252
253
254
255
256
257
258
259 public Object getNestedProperty(Object arg0, String arg1) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
260
261 try {
262 try {
263 return fastGetNestedProperty(arg0, arg1);
264 }
265 catch (Exception e) {
266 return super.getNestedProperty(arg0, arg1);
267 }
268 }
269 catch (NestedNullException e) {
270 return getUnreachableNestedProperty(arg0, arg1);
271 }
272 catch (InvocationTargetException e1) {
273 return getUnreachableNestedProperty(arg0, arg1);
274 }
275
276
277 }
278
279
280
281
282
283 public Object getIndexedProperty(Object bean, String name, int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
284 try {
285 return super.getIndexedProperty(bean, name, index);
286 } catch (IndexOutOfBoundsException ioobe) {
287 return generateIndexedProperty(bean, name, index, ioobe);
288 }
289 }
290
291 protected Object generateIndexedProperty(Object nestedBean, String property, int index, IndexOutOfBoundsException ioobe) throws IllegalAccessException, InvocationTargetException,
292 NoSuchMethodException {
293
294 if (!(nestedBean instanceof PersistableBusinessObject)) throw ioobe;
295
296
297 if (!List.class.isAssignableFrom(getPropertyType(nestedBean, property))) throw ioobe;
298
299 List list= (List) getProperty(nestedBean, property);
300
301 Class c = collectionItemClassProvider.getCollectionItemClass(nestedBean, property);
302
303 if (c == null) {
304 throw new RuntimeException("Unable to determined item class for collection '" + property + "' on bean of type '" + nestedBean.getClass() + "'");
305 }
306
307 Object value;
308 try {
309 value = c.newInstance();
310 } catch (InstantiationException ie) {
311 throw new RuntimeException("Error instantiating item class: " + c);
312 }
313
314
315 while (list.size() <= index) {
316 list.add(null);
317 }
318 list.set(index, value);
319
320 return super.getIndexedProperty(nestedBean, property, index);
321 }
322
323
324
325
326
327 private Object getUnreachableNestedProperty(Object arg0, String arg1) {
328 try {
329 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(arg0, arg1);
330 if (propertyDescriptor == null || Collection.class.isAssignableFrom(propertyDescriptor.getPropertyType())) {
331 return null;
332 }
333 } catch (IllegalAccessException e) {
334
335 } catch (InvocationTargetException e) {
336
337 } catch (NoSuchMethodException e) {
338
339 }
340
341 return "";
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362 public void setNestedProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
363
364 if (bean == null) {
365 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
366 return;
367 }
368 if (name == null) {
369 throw new IllegalArgumentException("No name specified");
370 }
371
372 Object propBean = null;
373 int indexOfINDEXED_DELIM = -1;
374 int indexOfMAPPED_DELIM = -1;
375 while (true) {
376 int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
377 if (delim < 0) {
378 break;
379 }
380 String next = name.substring(0, delim);
381 indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
382 indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
383 if (bean instanceof Map) {
384 propBean = ((Map) bean).get(next);
385 }
386 else if (indexOfMAPPED_DELIM >= 0) {
387 propBean = getMappedProperty(bean, next);
388 }
389 else if (indexOfINDEXED_DELIM >= 0) {
390 propBean = getIndexedProperty(bean, next);
391 }
392 else {
393 propBean = getSimpleProperty(bean, next);
394 }
395 if (ObjectUtils.isNull(propBean)) {
396 Class propertyType = getPropertyType(bean, next);
397 if (propertyType != null) {
398 Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
399 setSimpleProperty(bean, next, newInstance);
400 propBean = getSimpleProperty(bean, next);
401 }
402 }
403 bean = propBean;
404 name = name.substring(delim + 1);
405 }
406
407 indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
408 indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
409
410 if (bean instanceof Map) {
411
412 PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
413 if (descriptor == null) {
414
415 ((Map) bean).put(name, value);
416 }
417 else {
418
419 setSimpleProperty(bean, name, value);
420 }
421 }
422 else if (indexOfMAPPED_DELIM >= 0) {
423 setMappedProperty(bean, name, value);
424 }
425 else if (indexOfINDEXED_DELIM >= 0) {
426 setIndexedProperty(bean, name, value);
427 }
428 else {
429 setSimpleProperty(bean, name, value);
430 }
431 }
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456 public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
457 if (bean == null) {
458 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name);
459 return null;
460 }
461 if (name == null) {
462 throw new IllegalArgumentException("No name specified");
463 }
464 try {
465
466 Object propBean = null;
467 while (true) {
468 int delim = findNextNestedIndex(name);
469
470 if (delim < 0) {
471 break;
472 }
473 String next = name.substring(0, delim);
474 int indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
475 int indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
476 if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM < 0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) {
477 propBean = getMappedProperty(bean, next);
478 }
479 else {
480 if (indexOfINDEXED_DELIM >= 0) {
481 propBean = getIndexedProperty(bean, next);
482 }
483 else {
484 propBean = getSimpleProperty(bean, next);
485 }
486 }
487 if (ObjectUtils.isNull(propBean)) {
488 Class propertyType = getPropertyType(bean, next);
489 if (propertyType != null) {
490 Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
491 setSimpleProperty(bean, next, newInstance);
492 propBean = getSimpleProperty(bean, next);
493 }
494 }
495 bean = propBean;
496 name = name.substring(delim + 1);
497 }
498
499
500 int left = name.indexOf(PropertyUtils.INDEXED_DELIM);
501 if (left >= 0) {
502 name = name.substring(0, left);
503 }
504 left = name.indexOf(PropertyUtils.MAPPED_DELIM);
505 if (left >= 0) {
506 name = name.substring(0, left);
507 }
508
509
510
511 if ((bean == null) || (name == null)) {
512 return (null);
513 }
514
515 PropertyDescriptor descriptors[] = getPropertyDescriptors(bean);
516 if (descriptors != null) {
517
518 for (int i = 0; i < descriptors.length; i++) {
519 if (name.equals(descriptors[i].getName()))
520 return (descriptors[i]);
521 }
522 }
523
524 PropertyDescriptor result = null;
525 FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean);
526 if (mappedDescriptors == null) {
527 mappedDescriptors = new FastHashMap();
528 mappedDescriptors.setFast(true);
529 }
530 result = (PropertyDescriptor) mappedDescriptors.get(name);
531 if (result == null) {
532
533 try {
534 result = new MappedPropertyDescriptor(name, bean.getClass());
535 }
536 catch (IntrospectionException ie) {
537 }
538 if (result != null) {
539 mappedDescriptors.put(name, result);
540 }
541 }
542
543 return result;
544 } catch ( RuntimeException ex ) {
545 LOG.error( "Unable to get property descriptor for " + bean.getClass().getName() + " . " + name
546 + "\n" + ex.getClass().getName() + ": " + ex.getMessage() );
547 throw ex;
548 }
549 }
550
551
552 private int findNextNestedIndex(String expression)
553 {
554
555
556 int bracketCount = 0;
557 for (int i=0, size=expression.length(); i<size ; i++) {
558 char at = expression.charAt(i);
559 switch (at) {
560 case PropertyUtils.NESTED_DELIM:
561 if (bracketCount < 1) {
562 return i;
563 }
564 break;
565
566 case PropertyUtils.MAPPED_DELIM:
567 case PropertyUtils.INDEXED_DELIM:
568
569 ++bracketCount;
570 break;
571
572 case PropertyUtils.MAPPED_DELIM2:
573 case PropertyUtils.INDEXED_DELIM2:
574
575 --bracketCount;
576 break;
577 }
578 }
579
580 return -1;
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602 public void setSimpleProperty(Object bean,
603 String name, Object value)
604 throws IllegalAccessException, InvocationTargetException,
605 NoSuchMethodException {
606
607 if (bean == null) {
608 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
609 return;
610 }
611 if (name == null) {
612 throw new IllegalArgumentException("No name specified");
613 }
614
615
616 if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) {
617 throw new IllegalArgumentException
618 ("Nested property names are not allowed");
619 } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) {
620 throw new IllegalArgumentException
621 ("Indexed property names are not allowed");
622 } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) {
623 throw new IllegalArgumentException
624 ("Mapped property names are not allowed");
625 }
626
627
628 PropertyDescriptor descriptor =
629 getPropertyDescriptor(bean, name);
630 if (descriptor == null) {
631 throw new NoSuchMethodException("Unknown property '" +
632 name + "'");
633 }
634 Method writeMethod = getWriteMethod(descriptor);
635 if (writeMethod == null) {
636
637 LOG.warn("Bean: " + bean.getClass().getName() + ", Property '" + name + "' has no setter method");
638 return;
639 }
640
641
642 Object values[] = new Object[1];
643 values[0] = value;
644 if (LOG.isDebugEnabled()) {
645 String valueClassName =
646 value == null ? "<null>" : value.getClass().getName();
647 LOG.debug("setSimpleProperty: Invoking method " + writeMethod
648 + " with value " + value + " (class " + valueClassName + ")");
649 }
650
651
652 invokeMethod(writeMethod, bean, values);
653
654 }
655
656
657 private Object invokeMethod(
658 Method method,
659 Object bean,
660 Object[] values)
661 throws
662 IllegalAccessException,
663 InvocationTargetException {
664 try {
665
666 return method.invoke(bean, values);
667
668 } catch (IllegalArgumentException e) {
669
670 LOG.error("Method invocation failed.", e);
671 throw new IllegalArgumentException(
672 "Cannot invoke " + method.getDeclaringClass().getName() + "."
673 + method.getName() + " - " + e.getMessage());
674
675 }
676 }
677
678 public Class getPropertyType(Object bean, String name)
679 throws IllegalAccessException, InvocationTargetException,
680 NoSuchMethodException {
681
682 if (bean == null) {
683 throw new IllegalArgumentException("No bean specified");
684 }
685 if (name == null) {
686 throw new IllegalArgumentException("No name specified for bean class '" +
687 bean.getClass() + "'");
688 }
689
690
691 while (getResolver().hasNested(name)) {
692 String next = getResolver().next(name);
693 Object nestedBean = getProperty(bean, next);
694 if (nestedBean == null) {
695 Class<?>[] paramTypes = {};
696 Method method = null;
697 try {
698 method = bean.getClass().getMethod("get" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null);
699 } catch (NoSuchMethodException e) {
700 method = bean.getClass().getMethod("is" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null);
701 }
702 try {
703 nestedBean = ObjectUtils.createNewObjectFromClass(method.getReturnType());
704 } catch (RuntimeException e) {
705 NestedNullException nne = new NestedNullException
706 ("Null property value for '" + next +
707 "' on bean class '" + bean.getClass() + "'");
708 nne.initCause(e);
709 throw nne;
710 }
711 }
712 bean = nestedBean;
713 name = getResolver().remove(name);
714 }
715
716
717 name = getResolver().getProperty(name);
718
719
720 if (bean instanceof DynaBean) {
721 DynaProperty descriptor =
722 ((DynaBean) bean).getDynaClass().getDynaProperty(name);
723 if (descriptor == null) {
724 return (null);
725 }
726 Class type = descriptor.getType();
727 if (type == null) {
728 return (null);
729 } else if (type.isArray()) {
730 return (type.getComponentType());
731 } else {
732 return (type);
733 }
734 }
735
736 PropertyDescriptor descriptor =
737 getPropertyDescriptor(bean, name);
738 if (descriptor == null) {
739 return (null);
740 } else if (descriptor instanceof IndexedPropertyDescriptor) {
741 return (((IndexedPropertyDescriptor) descriptor).
742 getIndexedPropertyType());
743 } else if (descriptor instanceof MappedPropertyDescriptor) {
744 return (((MappedPropertyDescriptor) descriptor).
745 getMappedPropertyType());
746 } else {
747 return (descriptor.getPropertyType());
748 }
749
750 }
751 }