001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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:
017: package org.springframework.beans;
018:
019: import java.beans.PropertyChangeEvent;
020: import java.lang.reflect.Field;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.springframework.util.Assert;
025: import org.springframework.util.ReflectionUtils;
026:
027: /**
028: * PropertyAccessor implementation that directly accesses instance fields.
029: * Allows for direct binding to fields instead of going through JavaBean setters.
030: *
031: * <p>This implementation just supports fields in the actual target object.
032: * It is not able to traverse nested fields.
033: *
034: * <p>A DirectFieldAccessor's default for the "extractOldValueForEditor" setting
035: * is "true", since a field can always be read without side effects.
036: *
037: * @author Juergen Hoeller
038: * @since 2.0
039: * @see #setExtractOldValueForEditor
040: * @see BeanWrapper
041: * @see org.springframework.validation.DirectFieldBindingResult
042: * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
043: */
044: public class DirectFieldAccessor extends AbstractPropertyAccessor {
045:
046: private final Object target;
047:
048: private final Map fieldMap = new HashMap();
049:
050: private final TypeConverterDelegate typeConverterDelegate;
051:
052: /**
053: * Create a new DirectFieldAccessor for the given target object.
054: * @param target the target object to access
055: */
056: public DirectFieldAccessor(Object target) {
057: Assert.notNull(target, "Target object must not be null");
058: this .target = target;
059: ReflectionUtils.doWithFields(this .target.getClass(),
060: new ReflectionUtils.FieldCallback() {
061: public void doWith(Field field) {
062: fieldMap.put(field.getName(), field);
063: }
064: });
065: this .typeConverterDelegate = new TypeConverterDelegate(this ,
066: target);
067: setExtractOldValueForEditor(true);
068: }
069:
070: public boolean isReadableProperty(String propertyName)
071: throws BeansException {
072: return this .fieldMap.containsKey(propertyName);
073: }
074:
075: public boolean isWritableProperty(String propertyName)
076: throws BeansException {
077: return this .fieldMap.containsKey(propertyName);
078: }
079:
080: public Class getPropertyType(String propertyName)
081: throws BeansException {
082: Field field = (Field) this .fieldMap.get(propertyName);
083: if (field != null) {
084: return field.getType();
085: }
086: return null;
087: }
088:
089: public Object getPropertyValue(String propertyName)
090: throws BeansException {
091: Field field = (Field) this .fieldMap.get(propertyName);
092: if (field == null) {
093: throw new NotReadablePropertyException(this .target
094: .getClass(), propertyName, "Field '" + propertyName
095: + "' does not exist");
096: }
097: try {
098: ReflectionUtils.makeAccessible(field);
099: return field.get(this .target);
100: } catch (IllegalAccessException ex) {
101: throw new InvalidPropertyException(this .target.getClass(),
102: propertyName, "Field is not accessible", ex);
103: }
104: }
105:
106: public void setPropertyValue(String propertyName, Object newValue)
107: throws BeansException {
108: Field field = (Field) this .fieldMap.get(propertyName);
109: if (field == null) {
110: throw new NotWritablePropertyException(this .target
111: .getClass(), propertyName, "Field '" + propertyName
112: + "' does not exist");
113: }
114: Object oldValue = null;
115: try {
116: ReflectionUtils.makeAccessible(field);
117: oldValue = field.get(this .target);
118: Object convertedValue = this .typeConverterDelegate
119: .convertIfNecessary(propertyName, oldValue,
120: newValue, field.getType());
121: field.set(this .target, convertedValue);
122: } catch (IllegalAccessException ex) {
123: throw new InvalidPropertyException(this .target.getClass(),
124: propertyName, "Field is not accessible", ex);
125: } catch (IllegalArgumentException ex) {
126: PropertyChangeEvent pce = new PropertyChangeEvent(
127: this .target, propertyName, oldValue, newValue);
128: throw new TypeMismatchException(pce, field.getType(), ex);
129: }
130: }
131:
132: }
|