001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016// begin Kuali Foundation modification 017package org.kuali.rice.kns.web.struts.form.pojo; 018 019import org.apache.commons.beanutils.DynaBean; 020import org.apache.commons.beanutils.DynaProperty; 021import org.apache.commons.beanutils.MappedPropertyDescriptor; 022import org.apache.commons.beanutils.MethodUtils; 023import org.apache.commons.beanutils.NestedNullException; 024import org.apache.commons.beanutils.PropertyUtils; 025import org.apache.commons.beanutils.PropertyUtilsBean; 026import org.apache.commons.beanutils.WrapDynaBean; 027import org.apache.commons.collections.FastHashMap; 028import org.apache.log4j.Logger; 029import org.apache.ojb.broker.metadata.ClassDescriptor; 030import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException; 031import org.apache.ojb.broker.metadata.CollectionDescriptor; 032import org.apache.ojb.broker.metadata.DescriptorRepository; 033import org.apache.ojb.broker.metadata.MetadataManager; 034import org.kuali.rice.core.web.format.Formatter; 035import org.kuali.rice.krad.bo.PersistableBusinessObject; 036import org.kuali.rice.krad.service.KRADServiceLocator; 037import org.kuali.rice.krad.service.PersistenceStructureService; 038import org.kuali.rice.krad.util.ObjectUtils; 039 040import java.beans.IndexedPropertyDescriptor; 041import java.beans.IntrospectionException; 042import java.beans.PropertyDescriptor; 043import java.lang.reflect.InvocationTargetException; 044import java.lang.reflect.Method; 045import java.util.ArrayList; 046import java.util.Collection; 047import java.util.HashMap; 048import java.util.List; 049import java.util.Map; 050 051/** 052 * begin Kuali Foundation modification 053 * This class is used to access the properties of a Pojo bean. 054 * deleted author tag 055 * end Kuali Foundation modification 056 */ 057// Kuali Foundation modification: class originally SLPropertyUtilsBean 058public class PojoPropertyUtilsBean extends PropertyUtilsBean { 059 060 public static final Logger LOG = Logger.getLogger(PojoPropertyUtilsBean.class.getName()); 061 062 /** 063 * Thin interface for determining the appropriate item class for a collection property 064 */ 065 public static interface CollectionItemClassProvider { 066 public Class getCollectionItemClass(Object bean, String property); 067 } 068 069 /** 070 * CollectionItemClassProvider backed by OJB metadata 071 */ 072 public static class PersistenceStructureServiceProvider implements CollectionItemClassProvider { 073 protected static PersistenceStructureService persistenceStructureService = null; 074 protected static PersistenceStructureService getPersistenceStructureService() { 075 if (persistenceStructureService == null) { 076 persistenceStructureService = KRADServiceLocator.getPersistenceStructureService(); 077 } 078 return persistenceStructureService; 079 } 080 081 @Override 082 public Class getCollectionItemClass(Object bean, String property) { 083 Map<String, Class> collectionObjectTypes = getPersistenceStructureService().listCollectionObjectTypes(bean.getClass()); 084 return collectionObjectTypes.get(property); 085 } 086 } 087 088 // default is to consult OJB 089 protected static CollectionItemClassProvider collectionItemClassProvider = new PersistenceStructureServiceProvider(); 090 091 // begin Kuali Foundation modification 092 public PojoPropertyUtilsBean() { 093 super(); 094 } 095 // end Kuali Foundation modification 096 097 public Object getProperty(Object bean, String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 098 // begin Kuali Foundation modification 099 if (!(bean instanceof PojoForm)) 100 return super.getProperty(bean, key); 101 102 PojoForm form = (PojoForm) bean; 103 Map unconvertedValues = form.getUnconvertedValues(); 104 105 if (unconvertedValues.containsKey(key)) 106 return unconvertedValues.get(key); 107 108 Object val = getNestedProperty(bean, key); 109 Class type = (val!=null)?val.getClass():null; 110 if ( type == null ) { 111 try { 112 type = getPropertyType(bean, key); 113 } catch ( Exception ex ) { 114 type = String.class; 115 LOG.warn( "Unable to get property type for Class: " + bean.getClass().getName() + "/Property: " + key ); 116 } 117 } 118 return (Formatter.isSupportedType(type) ? form.formatValue(val, key, type) : val); 119 // end Kuali Foundation modification 120 } 121 122 // begin Kuali Foundation modification 123 private Map<String,List<Method>> cache = new HashMap<String, List<Method>>(); 124 private static Map<String,Method> readMethodCache = new HashMap<String, Method>(); 125 private IntrospectionException introspectionException = new IntrospectionException( "" ); 126 127 public Object fastGetNestedProperty(Object obj, String propertyName) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { 128 //logger.debug("entering fastGetNestedProperty"); 129 130 List<Method> methods = (List<Method>) cache.get(propertyName + obj.getClass().getName()); 131 if (methods == null) { 132 methods = new ArrayList<Method>(); 133 Object currentObj = obj; 134 Class<?> currentObjClass = currentObj.getClass(); 135 136 for (String currentPropertyName : propertyName.split("\\.") ) { 137 String cacheKey = currentObjClass.getName() + currentPropertyName; 138 Method readMethod = readMethodCache.get( cacheKey ); 139 if ( readMethod == null ) { 140 synchronized (readMethodCache) { 141 // if the read method was resolved to an error, repeat the exception 142 // rather than performing the reflection calls below 143 if ( readMethodCache.containsKey(cacheKey) ) { 144 throw introspectionException; 145 } 146 try { 147 try { 148 readMethod = currentObjClass.getMethod("get" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null); 149 } catch (NoSuchMethodException e) { 150 readMethod = currentObjClass.getMethod("is" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null); 151 } 152 } catch ( NoSuchMethodException ex ) { 153 // cache failures to prevent re-checking of the parameter 154 readMethodCache.put( cacheKey, null ); 155 throw introspectionException; 156 } 157 readMethodCache.put(cacheKey, readMethod ); 158 } 159 } 160 methods.add(readMethod); 161 currentObjClass = readMethod.getReturnType(); 162 } 163 synchronized (cache) { 164 cache.put(propertyName + obj.getClass().getName(), methods); 165 } 166 } 167 168 for ( Method method : methods ) { 169 obj = method.invoke(obj, (Object[])null); 170 } 171 172 //logger.debug("exiting fastGetNestedProperty"); 173 174 return obj; 175 } 176 // end Kuali Foundation modification 177 178 /* 179 * Kuali modification to make isWriteable work like it did in beanUtils 1.7. 180 * Checking for nested nulls caused exceptions in rice 2.0. 181 */ 182 @Override 183 public boolean isWriteable(Object bean, String name) { 184 // Validate method parameters 185 if (bean == null) { 186 throw new IllegalArgumentException("No bean specified"); 187 } 188 if (name == null) { 189 throw new IllegalArgumentException("No name specified for bean class '" + 190 bean.getClass() + "'"); 191 } 192 193 // Remove any subscript from the final name value 194 name = getResolver().getProperty(name); 195 196 // Treat WrapDynaBean as special case - may be a read-only property 197 // (see Jira issue# BEANUTILS-61) 198 if (bean instanceof WrapDynaBean) { 199 bean = ((WrapDynaBean)bean).getInstance(); 200 } 201 202 // Return the requested result 203 if (bean instanceof DynaBean) { 204 // All DynaBean properties are writeable 205 return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null); 206 } else { 207 try { 208 PropertyDescriptor desc = 209 getPropertyDescriptor(bean, name); 210 if (desc != null) { 211 Method writeMethod = desc.getWriteMethod(); 212 if (writeMethod == null) { 213 if (desc instanceof IndexedPropertyDescriptor) { 214 writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod(); 215 } else if (desc instanceof MappedPropertyDescriptor) { 216 writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod(); 217 } 218 writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod); 219 } 220 return (writeMethod != null); 221 } else { 222 return (false); 223 } 224 } catch (IllegalAccessException e) { 225 return (false); 226 } catch (InvocationTargetException e) { 227 return (false); 228 } catch (NoSuchMethodException e) { 229 return (false); 230 } 231 } 232 233 } 234 235 /** 236 * begin Kuali Foundation modification 237 * removed comments and @<no space>since javadoc attribute 238 * end Kuali Foundation modification 239 * @see org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty(java.lang.Object, java.lang.String) 240 */ 241 public Object getNestedProperty(Object arg0, String arg1) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 242 // begin Kuali Foundation modification 243 try { 244 try { 245 return fastGetNestedProperty(arg0, arg1); 246 } 247 catch (Exception e) { 248 return super.getNestedProperty(arg0, arg1); 249 } 250 } 251 catch (NestedNullException e) { 252 return getUnreachableNestedProperty(arg0, arg1); 253 } 254 catch (InvocationTargetException e1) { 255 return getUnreachableNestedProperty(arg0, arg1); 256 } 257 // removed commented code 258 // end Kuali Foundation modification 259 } 260 261 /** 262 * Customization of superclass getNestedProperty which transparently creates indexed property items 263 * {@inheritDoc} 264 */ 265 public Object getIndexedProperty(Object bean, String name, int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 266 try { 267 return super.getIndexedProperty(bean, name, index); 268 } catch (IndexOutOfBoundsException ioobe) { 269 return generateIndexedProperty(bean, name, index, ioobe); 270 } 271 } 272 273 protected Object generateIndexedProperty(Object nestedBean, String property, int index, IndexOutOfBoundsException ioobe) throws IllegalAccessException, InvocationTargetException, 274 NoSuchMethodException { 275 276 if (!(nestedBean instanceof PersistableBusinessObject)) throw ioobe; 277 278 // we can only grow lists 279 if (!List.class.isAssignableFrom(getPropertyType(nestedBean, property))) throw ioobe; 280 281 List list= (List) getProperty(nestedBean, property); 282 283 Class c = collectionItemClassProvider.getCollectionItemClass(nestedBean, property); 284 285 if (c == null) { 286 throw new RuntimeException("Unable to determined item class for collection '" + property + "' on bean of type '" + nestedBean.getClass() + "'"); 287 } 288 289 Object value; 290 try { 291 value = c.newInstance(); 292 } catch (InstantiationException ie) { 293 throw new RuntimeException("Error instantiating item class: " + c); 294 } 295 296 // fill any missing indices 297 while (list.size() <= index) { 298 list.add(null); 299 } 300 list.set(index, value); 301 302 return super.getIndexedProperty(nestedBean, property, index); 303 } 304 305 // begin Kuali Foundation modification 306 /** 307 * helper method makes sure we don't return "" for collections 308 */ 309 private Object getUnreachableNestedProperty(Object arg0, String arg1) { 310 try { 311 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(arg0, arg1); 312 if (propertyDescriptor == null || Collection.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { 313 return null; 314 } 315 } catch (IllegalAccessException e) { 316 // ignore 317 } catch (InvocationTargetException e) { 318 // ignore 319 } catch (NoSuchMethodException e) { 320 // ignore 321 } 322 323 return ""; 324 } 325 // end Kuali Foundation modification 326 327 328 // begin Kuali Foundation modification 329 /** 330 * begin Kuali Foundation modification 331 * Set the value of the (possibly nested) property of the specified name, for the specified bean, with no type conversions. 332 * 333 * @param bean Bean whose property is to be modified 334 * @param name Possibly nested name of the property to be modified 335 * @param value Value to which the property is to be set 336 * 337 * @exception IllegalAccessException if the caller does not have access to the property accessor method 338 * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null 339 * @exception IllegalArgumentException if a nested reference to a property returns null 340 * @exception InvocationTargetException if the property accessor method throws an exception 341 * @exception NoSuchMethodException if an accessor method for this propety cannot be found 342 * end Kuali Foundation modification 343 */ 344 public void setNestedProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 345 346 if (bean == null) { 347 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value); 348 return; 349 } 350 if (name == null) { 351 throw new IllegalArgumentException("No name specified"); 352 } 353 354 Object propBean = null; 355 int indexOfINDEXED_DELIM = -1; 356 int indexOfMAPPED_DELIM = -1; 357 while (true) { 358 int delim = name.indexOf(PropertyUtils.NESTED_DELIM); 359 if (delim < 0) { 360 break; 361 } 362 String next = name.substring(0, delim); 363 indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM); 364 indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM); 365 if (bean instanceof Map) { 366 propBean = ((Map) bean).get(next); 367 } 368 else if (indexOfMAPPED_DELIM >= 0) { 369 propBean = getMappedProperty(bean, next); 370 } 371 else if (indexOfINDEXED_DELIM >= 0) { 372 propBean = getIndexedProperty(bean, next); 373 } 374 else { 375 propBean = getSimpleProperty(bean, next); 376 } 377 if (ObjectUtils.isNull(propBean)) { 378 Class propertyType = getPropertyType(bean, next); 379 if (propertyType != null) { 380 Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType); 381 setSimpleProperty(bean, next, newInstance); 382 propBean = getSimpleProperty(bean, next); 383 } 384 } 385 bean = propBean; 386 name = name.substring(delim + 1); 387 } 388 389 indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM); 390 indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM); 391 392 if (bean instanceof Map) { 393 // check to see if the class has a standard property 394 PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); 395 if (descriptor == null) { 396 // no - then put the value into the map 397 ((Map) bean).put(name, value); 398 } 399 else { 400 // yes - use that instead 401 setSimpleProperty(bean, name, value); 402 } 403 } 404 else if (indexOfMAPPED_DELIM >= 0) { 405 setMappedProperty(bean, name, value); 406 } 407 else if (indexOfINDEXED_DELIM >= 0) { 408 setIndexedProperty(bean, name, value); 409 } 410 else { 411 setSimpleProperty(bean, name, value); 412 } 413 } 414 // end Kuali Foundation modification 415 416 // begin Kuali Foundation modification 417 /** 418 * <p> 419 * Retrieve the property descriptor for the specified property of the specified bean, or return <code>null</code> if there is 420 * no such descriptor. This method resolves indexed and nested property references in the same manner as other methods in this 421 * class, except that if the last (or only) name element is indexed, the descriptor for the last resolved property itself is 422 * returned. 423 * </p> 424 * 425 * <p> 426 * <strong>FIXME </strong>- Does not work with DynaBeans. 427 * </p> 428 * 429 * @param bean Bean for which a property descriptor is requested 430 * @param name Possibly indexed and/or nested name of the property for which a property descriptor is requested 431 * 432 * @exception IllegalAccessException if the caller does not have access to the property accessor method 433 * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null 434 * @exception IllegalArgumentException if a nested reference to a property returns null 435 * @exception InvocationTargetException if the property accessor method throws an exception 436 * @exception NoSuchMethodException if an accessor method for this propety cannot be found 437 */ 438 public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 439 if (bean == null) { 440 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name); 441 return null; 442 } 443 if (name == null) { 444 throw new IllegalArgumentException("No name specified"); 445 } 446 try { 447 // Resolve nested references 448 Object propBean = null; 449 while (true) { 450 int delim = findNextNestedIndex(name); 451 //int delim = name.indexOf(PropertyUtils.NESTED_DELIM); 452 if (delim < 0) { 453 break; 454 } 455 String next = name.substring(0, delim); 456 int indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM); 457 int indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM); 458 if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM < 0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) { 459 propBean = getMappedProperty(bean, next); 460 } 461 else { 462 if (indexOfINDEXED_DELIM >= 0) { 463 propBean = getIndexedProperty(bean, next); 464 } 465 else { 466 propBean = getSimpleProperty(bean, next); 467 } 468 } 469 if (ObjectUtils.isNull(propBean)) { 470 Class propertyType = getPropertyType(bean, next); 471 if (propertyType != null) { 472 Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType); 473 setSimpleProperty(bean, next, newInstance); 474 propBean = getSimpleProperty(bean, next); 475 } 476 } 477 bean = propBean; 478 name = name.substring(delim + 1); 479 } 480 481 // Remove any subscript from the final name value 482 int left = name.indexOf(PropertyUtils.INDEXED_DELIM); 483 if (left >= 0) { 484 name = name.substring(0, left); 485 } 486 left = name.indexOf(PropertyUtils.MAPPED_DELIM); 487 if (left >= 0) { 488 name = name.substring(0, left); 489 } 490 491 // Look up and return this property from our cache 492 // creating and adding it to the cache if not found. 493 if ((bean == null) || (name == null)) { 494 return (null); 495 } 496 497 PropertyDescriptor descriptors[] = getPropertyDescriptors(bean); 498 if (descriptors != null) { 499 500 for (int i = 0; i < descriptors.length; i++) { 501 if (name.equals(descriptors[i].getName())) 502 return (descriptors[i]); 503 } 504 } 505 506 PropertyDescriptor result = null; 507 FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean); 508 if (mappedDescriptors == null) { 509 mappedDescriptors = new FastHashMap(); 510 mappedDescriptors.setFast(true); 511 } 512 result = (PropertyDescriptor) mappedDescriptors.get(name); 513 if (result == null) { 514 // not found, try to create it 515 try { 516 result = new MappedPropertyDescriptor(name, bean.getClass()); 517 } 518 catch (IntrospectionException ie) { 519 } 520 if (result != null) { 521 mappedDescriptors.put(name, result); 522 } 523 } 524 525 return result; 526 } catch ( RuntimeException ex ) { 527 LOG.error( "Unable to get property descriptor for " + bean.getClass().getName() + " . " + name 528 + "\n" + ex.getClass().getName() + ": " + ex.getMessage() ); 529 throw ex; 530 } 531 } 532 // end Kuali Foundation modification 533 534 private int findNextNestedIndex(String expression) 535 { 536 // walk back from the end to the start 537 // and find the first index that 538 int bracketCount = 0; 539 for (int i=0, size=expression.length(); i<size ; i++) { 540 char at = expression.charAt(i); 541 switch (at) { 542 case PropertyUtils.NESTED_DELIM: 543 if (bracketCount < 1) { 544 return i; 545 } 546 break; 547 548 case PropertyUtils.MAPPED_DELIM: 549 case PropertyUtils.INDEXED_DELIM: 550 // not bothered which 551 ++bracketCount; 552 break; 553 554 case PropertyUtils.MAPPED_DELIM2: 555 case PropertyUtils.INDEXED_DELIM2: 556 // not bothered which 557 --bracketCount; 558 break; 559 } 560 } 561 // can't find any 562 return -1; 563 } 564 565 /** 566 * Set the value of the specified simple property of the specified bean, 567 * with no type conversions. 568 * 569 * @param bean Bean whose property is to be modified 570 * @param name Name of the property to be modified 571 * @param value Value to which the property should be set 572 * 573 * @exception IllegalAccessException if the caller does not have 574 * access to the property accessor method 575 * @exception IllegalArgumentException if <code>bean</code> or 576 * <code>name</code> is null 577 * @exception IllegalArgumentException if the property name is 578 * nested or indexed 579 * @exception InvocationTargetException if the property accessor method 580 * throws an exception 581 * @exception NoSuchMethodException if an accessor method for this 582 * propety cannot be found 583 */ 584 public void setSimpleProperty(Object bean, 585 String name, Object value) 586 throws IllegalAccessException, InvocationTargetException, 587 NoSuchMethodException { 588 589 if (bean == null) { 590 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value); 591 return; 592 } 593 if (name == null) { 594 throw new IllegalArgumentException("No name specified"); 595 } 596 597 // Validate the syntax of the property name 598 if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) { 599 throw new IllegalArgumentException 600 ("Nested property names are not allowed"); 601 } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) { 602 throw new IllegalArgumentException 603 ("Indexed property names are not allowed"); 604 } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) { 605 throw new IllegalArgumentException 606 ("Mapped property names are not allowed"); 607 } 608 609 // Retrieve the property setter method for the specified property 610 PropertyDescriptor descriptor = 611 getPropertyDescriptor(bean, name); 612 if (descriptor == null) { 613 throw new NoSuchMethodException("Unknown property '" + 614 name + "'"); 615 } 616 Method writeMethod = getWriteMethod(descriptor); 617 if (writeMethod == null) { 618 //throw new NoSuchMethodException("Property '" + name + "' has no setter method"); 619 LOG.warn("Bean: " + bean.getClass().getName() + ", Property '" + name + "' has no setter method"); 620 return; 621 } 622 623 // Call the property setter method 624 Object values[] = new Object[1]; 625 values[0] = value; 626 if (LOG.isDebugEnabled()) { 627 String valueClassName = 628 value == null ? "<null>" : value.getClass().getName(); 629 LOG.debug("setSimpleProperty: Invoking method " + writeMethod 630 + " with value " + value + " (class " + valueClassName + ")"); 631 } 632 633 634 invokeMethod(writeMethod, bean, values); 635 636 } 637 638 /** This just catches and wraps IllegalArgumentException. */ 639 private Object invokeMethod( 640 Method method, 641 Object bean, 642 Object[] values) 643 throws 644 IllegalAccessException, 645 InvocationTargetException { 646 try { 647 648 return method.invoke(bean, values); 649 650 } catch (IllegalArgumentException e) { 651 652 LOG.error("Method invocation failed.", e); 653 throw new IllegalArgumentException( 654 "Cannot invoke " + method.getDeclaringClass().getName() + "." 655 + method.getName() + " - " + e.getMessage()); 656 657 } 658 } 659 660 public Class getPropertyType(Object bean, String name) 661 throws IllegalAccessException, InvocationTargetException, 662 NoSuchMethodException { 663 664 if (bean == null) { 665 throw new IllegalArgumentException("No bean specified"); 666 } 667 if (name == null) { 668 throw new IllegalArgumentException("No name specified for bean class '" + 669 bean.getClass() + "'"); 670 } 671 672 // Resolve nested references 673 while (getResolver().hasNested(name)) { 674 String next = getResolver().next(name); 675 Object nestedBean = getProperty(bean, next); 676 if (nestedBean == null) { 677 Class<?>[] paramTypes = {}; 678 Method method = null; 679 try { 680 method = bean.getClass().getMethod("get" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null); 681 } catch (NoSuchMethodException e) { 682 method = bean.getClass().getMethod("is" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null); 683 } 684 try { 685 nestedBean = ObjectUtils.createNewObjectFromClass(method.getReturnType()); 686 } catch (RuntimeException e) { 687 NestedNullException nne = new NestedNullException 688 ("Null property value for '" + next + 689 "' on bean class '" + bean.getClass() + "'"); 690 nne.initCause(e); 691 throw nne; 692 } 693 } 694 bean = nestedBean; 695 name = getResolver().remove(name); 696 } 697 698 // Remove any subscript from the final name value 699 name = getResolver().getProperty(name); 700 701 // Special handling for DynaBeans 702 if (bean instanceof DynaBean) { 703 DynaProperty descriptor = 704 ((DynaBean) bean).getDynaClass().getDynaProperty(name); 705 if (descriptor == null) { 706 return (null); 707 } 708 Class type = descriptor.getType(); 709 if (type == null) { 710 return (null); 711 } else if (type.isArray()) { 712 return (type.getComponentType()); 713 } else { 714 return (type); 715 } 716 } 717 718 PropertyDescriptor descriptor = 719 getPropertyDescriptor(bean, name); 720 if (descriptor == null) { 721 return (null); 722 } else if (descriptor instanceof IndexedPropertyDescriptor) { 723 return (((IndexedPropertyDescriptor) descriptor). 724 getIndexedPropertyType()); 725 } else if (descriptor instanceof MappedPropertyDescriptor) { 726 return (((MappedPropertyDescriptor) descriptor). 727 getMappedPropertyType()); 728 } else { 729 return (descriptor.getPropertyType()); 730 } 731 732 } 733}