@@ -284,198 +284,212 @@ public void setPropertyValue(PropertyValue pv) throws BeansException {
284
284
}
285
285
}
286
286
287
- @ SuppressWarnings ("unchecked" )
288
287
protected void setPropertyValue (PropertyTokenHolder tokens , PropertyValue pv ) throws BeansException {
289
- String propertyName = tokens .canonicalName ;
290
- String actualName = tokens .actualName ;
291
-
292
288
if (tokens .keys != null ) {
293
- // Apply indexes and map keys: fetch value for all keys but the last one.
294
- PropertyTokenHolder getterTokens = new PropertyTokenHolder ();
295
- getterTokens .canonicalName = tokens .canonicalName ;
296
- getterTokens .actualName = tokens .actualName ;
297
- getterTokens .keys = new String [tokens .keys .length - 1 ];
298
- System .arraycopy (tokens .keys , 0 , getterTokens .keys , 0 , tokens .keys .length - 1 );
299
- Object propValue ;
289
+ processKeyedProperty (tokens , pv );
290
+ }
291
+ else {
292
+ processLocalProperty (tokens , pv );
293
+ }
294
+ }
295
+
296
+ @ SuppressWarnings ("unchecked" )
297
+ private void processKeyedProperty (PropertyTokenHolder tokens , PropertyValue pv ) {
298
+ Object propValue = getPropertyHoldingValue (tokens );
299
+ String lastKey = tokens .keys [tokens .keys .length - 1 ];
300
+
301
+ if (propValue .getClass ().isArray ()) {
302
+ PropertyHandler ph = getLocalPropertyHandler (tokens .actualName );
303
+ Class <?> requiredType = propValue .getClass ().getComponentType ();
304
+ int arrayIndex = Integer .parseInt (lastKey );
305
+ Object oldValue = null ;
300
306
try {
301
- propValue = getPropertyValue (getterTokens );
302
- }
303
- catch (NotReadablePropertyException ex ) {
304
- throw new NotWritablePropertyException (getRootClass (), this .nestedPath + propertyName ,
305
- "Cannot access indexed value in property referenced " +
306
- "in indexed property path '" + propertyName + "'" , ex );
307
- }
308
- // Set value for last key.
309
- String key = tokens .keys [tokens .keys .length - 1 ];
310
- if (propValue == null ) {
311
- // null map value case
312
- if (isAutoGrowNestedPaths ()) {
313
- // TODO: cleanup, this is pretty hacky
314
- int lastKeyIndex = tokens .canonicalName .lastIndexOf ('[' );
315
- getterTokens .canonicalName = tokens .canonicalName .substring (0 , lastKeyIndex );
316
- propValue = setDefaultValue (getterTokens );
307
+ if (isExtractOldValueForEditor () && arrayIndex < Array .getLength (propValue )) {
308
+ oldValue = Array .get (propValue , arrayIndex );
317
309
}
318
- else {
319
- throw new NullValueInNestedPathException (getRootClass (), this .nestedPath + propertyName ,
320
- "Cannot access indexed value in property referenced " +
321
- "in indexed property path '" + propertyName + "': returned null" );
310
+ Object convertedValue = convertIfNecessary (tokens .canonicalName , oldValue , pv .getValue (),
311
+ requiredType , ph .nested (tokens .keys .length ));
312
+ int length = Array .getLength (propValue );
313
+ if (arrayIndex >= length && arrayIndex < this .autoGrowCollectionLimit ) {
314
+ Class <?> componentType = propValue .getClass ().getComponentType ();
315
+ Object newArray = Array .newInstance (componentType , arrayIndex + 1 );
316
+ System .arraycopy (propValue , 0 , newArray , 0 , length );
317
+ setPropertyValue (tokens .actualName , newArray );
318
+ propValue = getPropertyValue (tokens .actualName );
322
319
}
320
+ Array .set (propValue , arrayIndex , convertedValue );
323
321
}
324
- if (propValue .getClass ().isArray ()) {
325
- PropertyHandler ph = getLocalPropertyHandler (actualName );
326
- Class <?> requiredType = propValue .getClass ().getComponentType ();
327
- int arrayIndex = Integer .parseInt (key );
328
- Object oldValue = null ;
329
- try {
330
- if (isExtractOldValueForEditor () && arrayIndex < Array .getLength (propValue )) {
331
- oldValue = Array .get (propValue , arrayIndex );
322
+ catch (IndexOutOfBoundsException ex ) {
323
+ throw new InvalidPropertyException (getRootClass (), this .nestedPath + tokens .canonicalName ,
324
+ "Invalid array index in property path '" + tokens .canonicalName + "'" , ex );
325
+ }
326
+ }
327
+
328
+ else if (propValue instanceof List ) {
329
+ PropertyHandler ph = getPropertyHandler (tokens .actualName );
330
+ Class <?> requiredType = ph .getCollectionType (tokens .keys .length );
331
+ List <Object > list = (List <Object >) propValue ;
332
+ int index = Integer .parseInt (lastKey );
333
+ Object oldValue = null ;
334
+ if (isExtractOldValueForEditor () && index < list .size ()) {
335
+ oldValue = list .get (index );
336
+ }
337
+ Object convertedValue = convertIfNecessary (tokens .canonicalName , oldValue , pv .getValue (),
338
+ requiredType , ph .nested (tokens .keys .length ));
339
+ int size = list .size ();
340
+ if (index >= size && index < this .autoGrowCollectionLimit ) {
341
+ for (int i = size ; i < index ; i ++) {
342
+ try {
343
+ list .add (null );
332
344
}
333
- Object convertedValue = convertIfNecessary (propertyName , oldValue , pv .getValue (),
334
- requiredType , ph .nested (tokens .keys .length ));
335
- int length = Array .getLength (propValue );
336
- if (arrayIndex >= length && arrayIndex < this .autoGrowCollectionLimit ) {
337
- Class <?> componentType = propValue .getClass ().getComponentType ();
338
- Object newArray = Array .newInstance (componentType , arrayIndex + 1 );
339
- System .arraycopy (propValue , 0 , newArray , 0 , length );
340
- setPropertyValue (actualName , newArray );
341
- propValue = getPropertyValue (actualName );
345
+ catch (NullPointerException ex ) {
346
+ throw new InvalidPropertyException (getRootClass (), this .nestedPath + tokens .canonicalName ,
347
+ "Cannot set element with index " + index + " in List of size " +
348
+ size + ", accessed using property path '" + tokens .canonicalName +
349
+ "': List does not support filling up gaps with null elements" );
342
350
}
343
- Array .set (propValue , arrayIndex , convertedValue );
344
- }
345
- catch (IndexOutOfBoundsException ex ) {
346
- throw new InvalidPropertyException (getRootClass (), this .nestedPath + propertyName ,
347
- "Invalid array index in property path '" + propertyName + "'" , ex );
348
351
}
352
+ list .add (convertedValue );
349
353
}
350
- else if (propValue instanceof List ) {
351
- PropertyHandler ph = getPropertyHandler (actualName );
352
- Class <?> requiredType = ph .getCollectionType (tokens .keys .length );
353
- List <Object > list = (List <Object >) propValue ;
354
- int index = Integer .parseInt (key );
355
- Object oldValue = null ;
356
- if (isExtractOldValueForEditor () && index < list .size ()) {
357
- oldValue = list .get (index );
358
- }
359
- Object convertedValue = convertIfNecessary (propertyName , oldValue , pv .getValue (),
360
- requiredType , ph .nested (tokens .keys .length ));
361
- int size = list .size ();
362
- if (index >= size && index < this .autoGrowCollectionLimit ) {
363
- for (int i = size ; i < index ; i ++) {
364
- try {
365
- list .add (null );
366
- }
367
- catch (NullPointerException ex ) {
368
- throw new InvalidPropertyException (getRootClass (), this .nestedPath + propertyName ,
369
- "Cannot set element with index " + index + " in List of size " +
370
- size + ", accessed using property path '" + propertyName +
371
- "': List does not support filling up gaps with null elements" );
372
- }
373
- }
374
- list .add (convertedValue );
354
+ else {
355
+ try {
356
+ list .set (index , convertedValue );
375
357
}
376
- else {
377
- try {
378
- list .set (index , convertedValue );
379
- }
380
- catch (IndexOutOfBoundsException ex ) {
381
- throw new InvalidPropertyException (getRootClass (), this .nestedPath + propertyName ,
382
- "Invalid list index in property path '" + propertyName + "'" , ex );
383
- }
358
+ catch (IndexOutOfBoundsException ex ) {
359
+ throw new InvalidPropertyException (getRootClass (), this .nestedPath + tokens .canonicalName ,
360
+ "Invalid list index in property path '" + tokens .canonicalName + "'" , ex );
384
361
}
385
362
}
386
- else if (propValue instanceof Map ) {
387
- PropertyHandler ph = getLocalPropertyHandler (actualName );
388
- Class <?> mapKeyType = ph .getMapKeyType (tokens .keys .length );
389
- Class <?> mapValueType = ph .getMapValueType (tokens .keys .length );
390
- Map <Object , Object > map = (Map <Object , Object >) propValue ;
391
- // IMPORTANT: Do not pass full property name in here - property editors
392
- // must not kick in for map keys but rather only for map values.
393
- TypeDescriptor typeDescriptor = TypeDescriptor .valueOf (mapKeyType );
394
- Object convertedMapKey = convertIfNecessary (null , null , key , mapKeyType , typeDescriptor );
395
- Object oldValue = null ;
396
- if (isExtractOldValueForEditor ()) {
397
- oldValue = map .get (convertedMapKey );
363
+ }
364
+
365
+ else if (propValue instanceof Map ) {
366
+ PropertyHandler ph = getLocalPropertyHandler (tokens .actualName );
367
+ Class <?> mapKeyType = ph .getMapKeyType (tokens .keys .length );
368
+ Class <?> mapValueType = ph .getMapValueType (tokens .keys .length );
369
+ Map <Object , Object > map = (Map <Object , Object >) propValue ;
370
+ // IMPORTANT: Do not pass full property name in here - property editors
371
+ // must not kick in for map keys but rather only for map values.
372
+ TypeDescriptor typeDescriptor = TypeDescriptor .valueOf (mapKeyType );
373
+ Object convertedMapKey = convertIfNecessary (null , null , lastKey , mapKeyType , typeDescriptor );
374
+ Object oldValue = null ;
375
+ if (isExtractOldValueForEditor ()) {
376
+ oldValue = map .get (convertedMapKey );
377
+ }
378
+ // Pass full property name and old value in here, since we want full
379
+ // conversion ability for map values.
380
+ Object convertedMapValue = convertIfNecessary (tokens .canonicalName , oldValue , pv .getValue (),
381
+ mapValueType , ph .nested (tokens .keys .length ));
382
+ map .put (convertedMapKey , convertedMapValue );
383
+ }
384
+
385
+ else {
386
+ throw new InvalidPropertyException (getRootClass (), this .nestedPath + tokens .canonicalName ,
387
+ "Property referenced in indexed property path '" + tokens .canonicalName +
388
+ "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]" );
389
+ }
390
+ }
391
+
392
+ private Object getPropertyHoldingValue (PropertyTokenHolder tokens ) {
393
+ // Apply indexes and map keys: fetch value for all keys but the last one.
394
+ PropertyTokenHolder getterTokens = new PropertyTokenHolder ();
395
+ getterTokens .canonicalName = tokens .canonicalName ;
396
+ getterTokens .actualName = tokens .actualName ;
397
+ getterTokens .keys = new String [tokens .keys .length - 1 ];
398
+ System .arraycopy (tokens .keys , 0 , getterTokens .keys , 0 , tokens .keys .length - 1 );
399
+
400
+ Object propValue ;
401
+ try {
402
+ propValue = getPropertyValue (getterTokens );
403
+ }
404
+ catch (NotReadablePropertyException ex ) {
405
+ throw new NotWritablePropertyException (getRootClass (), this .nestedPath + tokens .canonicalName ,
406
+ "Cannot access indexed value in property referenced " +
407
+ "in indexed property path '" + tokens .canonicalName + "'" , ex );
408
+ }
409
+
410
+ if (propValue == null ) {
411
+ // null map value case
412
+ if (isAutoGrowNestedPaths ()) {
413
+ int lastKeyIndex = tokens .canonicalName .lastIndexOf ('[' );
414
+ getterTokens .canonicalName = tokens .canonicalName .substring (0 , lastKeyIndex );
415
+ propValue = setDefaultValue (getterTokens );
416
+ }
417
+ else {
418
+ throw new NullValueInNestedPathException (getRootClass (), this .nestedPath + tokens .canonicalName ,
419
+ "Cannot access indexed value in property referenced " +
420
+ "in indexed property path '" + tokens .canonicalName + "': returned null" );
421
+ }
422
+ }
423
+ return propValue ;
424
+ }
425
+
426
+ private void processLocalProperty (PropertyTokenHolder tokens , PropertyValue pv ) {
427
+ PropertyHandler ph = getLocalPropertyHandler (tokens .actualName );
428
+ if (ph == null || !ph .isWritable ()) {
429
+ if (pv .isOptional ()) {
430
+ if (logger .isDebugEnabled ()) {
431
+ logger .debug ("Ignoring optional value for property '" + tokens .actualName +
432
+ "' - property not found on bean class [" + getRootClass ().getName () + "]" );
398
433
}
399
- // Pass full property name and old value in here, since we want full
400
- // conversion ability for map values.
401
- Object convertedMapValue = convertIfNecessary (propertyName , oldValue , pv .getValue (),
402
- mapValueType , ph .nested (tokens .keys .length ));
403
- map .put (convertedMapKey , convertedMapValue );
434
+ return ;
404
435
}
405
436
else {
406
- throw new InvalidPropertyException (getRootClass (), this .nestedPath + propertyName ,
407
- "Property referenced in indexed property path '" + propertyName +
408
- "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]" );
437
+ throw createNotWritablePropertyException (tokens .canonicalName );
409
438
}
410
439
}
411
440
412
- else {
413
- PropertyHandler ph = getLocalPropertyHandler (actualName );
414
- if (ph == null || !ph .isWritable ()) {
415
- if (pv .isOptional ()) {
416
- if (logger .isDebugEnabled ()) {
417
- logger .debug ("Ignoring optional value for property '" + actualName +
418
- "' - property not found on bean class [" + getRootClass ().getName () + "]" );
419
- }
420
- return ;
441
+ Object oldValue = null ;
442
+ try {
443
+ Object originalValue = pv .getValue ();
444
+ Object valueToApply = originalValue ;
445
+ if (!Boolean .FALSE .equals (pv .conversionNecessary )) {
446
+ if (pv .isConverted ()) {
447
+ valueToApply = pv .getConvertedValue ();
421
448
}
422
449
else {
423
- throw createNotWritablePropertyException (propertyName );
424
- }
425
- }
426
- Object oldValue = null ;
427
- try {
428
- Object originalValue = pv .getValue ();
429
- Object valueToApply = originalValue ;
430
- if (!Boolean .FALSE .equals (pv .conversionNecessary )) {
431
- if (pv .isConverted ()) {
432
- valueToApply = pv .getConvertedValue ();
433
- }
434
- else {
435
- if (isExtractOldValueForEditor () && ph .isReadable ()) {
436
- try {
437
- oldValue = ph .getValue ();
450
+ if (isExtractOldValueForEditor () && ph .isReadable ()) {
451
+ try {
452
+ oldValue = ph .getValue ();
453
+ }
454
+ catch (Exception ex ) {
455
+ if (ex instanceof PrivilegedActionException ) {
456
+ ex = ((PrivilegedActionException ) ex ).getException ();
438
457
}
439
- catch (Exception ex ) {
440
- if (ex instanceof PrivilegedActionException ) {
441
- ex = ((PrivilegedActionException ) ex ).getException ();
442
- }
443
- if (logger .isDebugEnabled ()) {
444
- logger .debug ("Could not read previous value of property '" +
445
- this .nestedPath + propertyName + "'" , ex );
446
- }
458
+ if (logger .isDebugEnabled ()) {
459
+ logger .debug ("Could not read previous value of property '" +
460
+ this .nestedPath + tokens .canonicalName + "'" , ex );
447
461
}
448
462
}
449
- valueToApply = convertForProperty (
450
- propertyName , oldValue , originalValue , ph .toTypeDescriptor ());
451
463
}
452
- pv .getOriginalPropertyValue ().conversionNecessary = (valueToApply != originalValue );
464
+ valueToApply = convertForProperty (
465
+ tokens .canonicalName , oldValue , originalValue , ph .toTypeDescriptor ());
453
466
}
454
- ph . setValue ( this . wrappedObject , valueToApply );
467
+ pv . getOriginalPropertyValue (). conversionNecessary = ( valueToApply != originalValue );
455
468
}
456
- catch (TypeMismatchException ex ) {
457
- throw ex ;
469
+ ph .setValue (this .wrappedObject , valueToApply );
470
+ }
471
+ catch (TypeMismatchException ex ) {
472
+ throw ex ;
473
+ }
474
+ catch (InvocationTargetException ex ) {
475
+ PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent (
476
+ this .rootObject , this .nestedPath + tokens .canonicalName , oldValue , pv .getValue ());
477
+ if (ex .getTargetException () instanceof ClassCastException ) {
478
+ throw new TypeMismatchException (propertyChangeEvent , ph .getPropertyType (), ex .getTargetException ());
458
479
}
459
- catch (InvocationTargetException ex ) {
460
- PropertyChangeEvent propertyChangeEvent =
461
- new PropertyChangeEvent (this .rootObject , this .nestedPath + propertyName , oldValue , pv .getValue ());
462
- if (ex .getTargetException () instanceof ClassCastException ) {
463
- throw new TypeMismatchException (propertyChangeEvent , ph .getPropertyType (), ex .getTargetException ());
464
- }
465
- else {
466
- Throwable cause = ex .getTargetException ();
467
- if (cause instanceof UndeclaredThrowableException ) {
468
- // May happen e.g. with Groovy-generated methods
469
- cause = cause .getCause ();
470
- }
471
- throw new MethodInvocationException (propertyChangeEvent , cause );
480
+ else {
481
+ Throwable cause = ex .getTargetException ();
482
+ if (cause instanceof UndeclaredThrowableException ) {
483
+ // May happen e.g. with Groovy-generated methods
484
+ cause = cause .getCause ();
472
485
}
486
+ throw new MethodInvocationException (propertyChangeEvent , cause );
473
487
}
474
- catch ( Exception ex ) {
475
- PropertyChangeEvent pce =
476
- new PropertyChangeEvent ( this . rootObject , this . nestedPath + propertyName , oldValue , pv . getValue ());
477
- throw new MethodInvocationException ( pce , ex );
478
- }
488
+ }
489
+ catch ( Exception ex ) {
490
+ PropertyChangeEvent pce = new PropertyChangeEvent (
491
+ this . rootObject , this . nestedPath + tokens . canonicalName , oldValue , pv . getValue () );
492
+ throw new MethodInvocationException ( pce , ex );
479
493
}
480
494
}
481
495
@@ -973,9 +987,6 @@ public String toString() {
973
987
}
974
988
975
989
976
- /**
977
- * Handle a given property.
978
- */
979
990
protected abstract static class PropertyHandler {
980
991
981
992
private final Class <?> propertyType ;
0 commit comments