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