@Override public void setPropertyValue(PropertyValue pv) throws BeansException { PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens; if (tokens == null) { String propertyName = pv.getName(); BeanWrapperImpl nestedBw; try { nestedBw = getBeanWrapperForPropertyPath(propertyName); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", ex); } tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName)); if (nestedBw == this) { pv.getOriginalPropertyValue().resolvedTokens = tokens; } nestedBw.setPropertyValue(tokens, pv); } else { setPropertyValue(tokens, pv); } }
@SuppressWarnings("unchecked") private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; if (tokens.keys != null) { // Apply indexes and map keys: fetch value for all keys but the last one. PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); Object propValue; try { propValue = getPropertyValue(getterTokens); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "'", ex); } // Set value for last key. String key = tokens.keys[tokens.keys.length - 1]; if (propValue == null) { // null map value case if (this.autoGrowNestedPaths) { // TODO: cleanup, this is pretty hacky int lastKeyIndex = tokens.canonicalName.lastIndexOf('['); getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex); propValue = setDefaultValue(getterTokens); } else { throw new NullValueInNestedPathException( getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "': returned null"); } } if (propValue.getClass().isArray()) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class requiredType = propValue.getClass().getComponentType(); int arrayIndex = Integer.parseInt(key); Object oldValue = null; try { if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) { oldValue = Array.get(propValue, arrayIndex); } Object convertedValue = convertIfNecessary( propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length)); Array.set(propValue, arrayIndex, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException( getRootClass(), this.nestedPath + propertyName, "Invalid array index in property path '" + propertyName + "'", ex); } } else if (propValue instanceof List) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class requiredType = GenericCollectionTypeResolver.getCollectionReturnType( pd.getReadMethod(), tokens.keys.length); List list = (List) propValue; int index = Integer.parseInt(key); int size = list.size(); Object oldValue = null; if (isExtractOldValueForEditor() && index < size) { oldValue = list.get(index); } Object convertedValue = convertIfNecessary( propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length)); if (index >= size && index < this.autoGrowCollectionLimit) { for (int i = size; i < index; i++) { try { list.add(null); } catch (NullPointerException ex) { throw new InvalidPropertyException( getRootClass(), this.nestedPath + propertyName, "Cannot set element with index " + index + " in List of size " + size + ", accessed using property path '" + propertyName + "': List does not support filling up gaps with null elements"); } } list.add(convertedValue); } else { try { list.set(index, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException( getRootClass(), this.nestedPath + propertyName, "Invalid list index in property path '" + propertyName + "'", ex); } } } else if (propValue instanceof Map) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType( pd.getReadMethod(), tokens.keys.length); Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType( pd.getReadMethod(), tokens.keys.length); Map map = (Map) propValue; // IMPORTANT: Do not pass full property name in here - property editors // must not kick in for map keys but rather only for map values. TypeDescriptor typeDescriptor = mapKeyType != null ? TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); Object oldValue = null; if (isExtractOldValueForEditor()) { oldValue = map.get(convertedMapKey); } // Pass full property name and old value in here, since we want full // conversion ability for map values. Object convertedMapValue = convertIfNecessary( propertyName, oldValue, pv.getValue(), mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length)); map.put(convertedMapKey, convertedMapValue); } else { throw new InvalidPropertyException( getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]"); } } else { PropertyDescriptor pd = pv.resolvedDescriptor; if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) { pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); if (pd == null || pd.getWriteMethod() == null) { if (pv.isOptional()) { logger.debug( "Ignoring optional value for property '" + actualName + "' - property not found on bean class [" + getRootClass().getName() + "]"); return; } else { PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, matches.buildErrorMessage(), matches.getPossibleMatches()); } } pv.getOriginalPropertyValue().resolvedDescriptor = pd; } Object oldValue = null; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; if (!Boolean.FALSE.equals(pv.conversionNecessary)) { if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { if (isExtractOldValueForEditor() && pd.getReadMethod() != null) { final Method readMethod = pd.getReadMethod(); if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) { if (System.getSecurityManager() != null) { AccessController.doPrivileged( new PrivilegedAction<Object>() { public Object run() { readMethod.setAccessible(true); return null; } }); } else { readMethod.setAccessible(true); } } try { if (System.getSecurityManager() != null) { oldValue = AccessController.doPrivileged( new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return readMethod.invoke(object); } }, acc); } else { oldValue = readMethod.invoke(object); } } catch (Exception ex) { if (ex instanceof PrivilegedActionException) { ex = ((PrivilegedActionException) ex).getException(); } if (logger.isDebugEnabled()) { logger.debug( "Could not read previous value of property '" + this.nestedPath + propertyName + "'", ex); } } } valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd); } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() : pd.getWriteMethod()); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { if (System.getSecurityManager() != null) { AccessController.doPrivileged( new PrivilegedAction<Object>() { public Object run() { writeMethod.setAccessible(true); return null; } }); } else { writeMethod.setAccessible(true); } } final Object value = valueToApply; if (System.getSecurityManager() != null) { try { AccessController.doPrivileged( new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { writeMethod.invoke(object, value); return null; } }, acc); } catch (PrivilegedActionException ex) { throw ex.getException(); } } else { writeMethod.invoke(this.object, value); } } catch (TypeMismatchException ex) { throw ex; } catch (InvocationTargetException ex) { PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent( this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); if (ex.getTargetException() instanceof ClassCastException) { throw new TypeMismatchException( propertyChangeEvent, pd.getPropertyType(), ex.getTargetException()); } else { throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException()); } } catch (Exception ex) { PropertyChangeEvent pce = new PropertyChangeEvent( this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); throw new MethodInvocationException(pce, ex); } } }