001: /*
002: * Copyright 2002-2007 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.PropertyDescriptor;
020: import java.io.Serializable;
021:
022: import org.springframework.util.Assert;
023: import org.springframework.util.ObjectUtils;
024:
025: /**
026: * Object to hold information and value for an individual bean property.
027: * Using an object here, rather than just storing all properties in
028: * a map keyed by property name, allows for more flexibility, and the
029: * ability to handle indexed properties etc in an optimized way.
030: *
031: * <p>Note that the value doesn't need to be the final required type:
032: * A {@link BeanWrapper} implementation should handle any necessary conversion,
033: * as this object doesn't know anything about the objects it will be applied to.
034: *
035: * @author Rod Johnson
036: * @author Rob Harrop
037: * @author Juergen Hoeller
038: * @since 13 May 2001
039: * @see PropertyValues
040: * @see BeanWrapper
041: */
042: public class PropertyValue extends BeanMetadataAttributeAccessor
043: implements Serializable {
044:
045: private final String name;
046:
047: private final Object value;
048:
049: private Object source;
050:
051: private boolean converted = false;
052:
053: private Object convertedValue;
054:
055: /** Package-visible field that indicates whether conversion is necessary */
056: volatile Boolean conversionNecessary;
057:
058: /** Package-visible field for caching the resolved property path tokens */
059: volatile Object resolvedTokens;
060:
061: /** Package-visible field for caching the resolved PropertyDescriptor */
062: volatile PropertyDescriptor resolvedDescriptor;
063:
064: /**
065: * Create a new PropertyValue instance.
066: * @param name the name of the property (never <code>null</code>)
067: * @param value the value of the property (possibly before type conversion)
068: */
069: public PropertyValue(String name, Object value) {
070: this .name = name;
071: this .value = value;
072: }
073:
074: /**
075: * Copy constructor.
076: * @param original the PropertyValue to copy (never <code>null</code>)
077: */
078: public PropertyValue(PropertyValue original) {
079: Assert.notNull(original, "Original must not be null");
080: this .name = original.getName();
081: this .value = original.getValue();
082: this .source = original.getSource();
083: this .conversionNecessary = original.conversionNecessary;
084: this .resolvedTokens = original.resolvedTokens;
085: this .resolvedDescriptor = original.resolvedDescriptor;
086: copyAttributesFrom(original);
087: }
088:
089: /**
090: * Constructor that exposes a new value for an original value holder.
091: * The original holder will be exposed as source of the new holder.
092: * @param original the PropertyValue to link to (never <code>null</code>)
093: * @param newValue the new value to apply
094: */
095: public PropertyValue(PropertyValue original, Object newValue) {
096: Assert.notNull(original, "Original must not be null");
097: this .name = original.getName();
098: this .value = newValue;
099: this .source = original;
100: this .conversionNecessary = original.conversionNecessary;
101: this .resolvedTokens = original.resolvedTokens;
102: this .resolvedDescriptor = original.resolvedDescriptor;
103: copyAttributesFrom(original);
104: }
105:
106: /**
107: * Return the name of the property.
108: */
109: public String getName() {
110: return this .name;
111: }
112:
113: /**
114: * Return the value of the property.
115: * <p>Note that type conversion will <i>not</i> have occurred here.
116: * It is the responsibility of the BeanWrapper implementation to
117: * perform type conversion.
118: */
119: public Object getValue() {
120: return this .value;
121: }
122:
123: /**
124: * Return the original PropertyValue instance for this value holder.
125: * @return the original PropertyValue (either a source of this
126: * value holder or this value holder itself).
127: */
128: public PropertyValue getOriginalPropertyValue() {
129: PropertyValue original = this ;
130: while (original.source instanceof PropertyValue
131: && original.source != original) {
132: original = (PropertyValue) original.source;
133: }
134: return original;
135: }
136:
137: /**
138: * Return whether this holder contains a converted value already (<code>true</code>),
139: * or whether the value still needs to be converted (<code>false</code>).
140: */
141: public synchronized boolean isConverted() {
142: return this .converted;
143: }
144:
145: /**
146: * Set the converted value of the constructor argument,
147: * after processed type conversion.
148: */
149: public synchronized void setConvertedValue(Object value) {
150: this .converted = true;
151: this .convertedValue = value;
152: }
153:
154: /**
155: * Return the converted value of the constructor argument,
156: * after processed type conversion.
157: */
158: public synchronized Object getConvertedValue() {
159: return this .convertedValue;
160: }
161:
162: public boolean equals(Object other) {
163: if (this == other) {
164: return true;
165: }
166: if (!(other instanceof PropertyValue)) {
167: return false;
168: }
169: PropertyValue otherPv = (PropertyValue) other;
170: return (this .name.equals(otherPv.name)
171: && ObjectUtils
172: .nullSafeEquals(this .value, otherPv.value) && ObjectUtils
173: .nullSafeEquals(this .source, otherPv.source));
174: }
175:
176: public int hashCode() {
177: return this .name.hashCode() * 29
178: + ObjectUtils.nullSafeHashCode(this .value);
179: }
180:
181: public String toString() {
182: return "bean property '" + this .name + "'";
183: }
184:
185: }
|