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