View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.beans;
18  
19  import java.beans.PropertyDescriptor;
20  import java.io.Serializable;
21  
22  import org.springframework.util.Assert;
23  import org.springframework.util.ObjectUtils;
24  
25  /**
26   * Object to hold information and value for an individual bean property.
27   * Using an object here, rather than just storing all properties in
28   * a map keyed by property name, allows for more flexibility, and the
29   * ability to handle indexed properties etc in an optimized way.
30   *
31   * <p>Note that the value doesn't need to be the final required type:
32   * A {@link BeanWrapper} implementation should handle any necessary conversion,
33   * as this object doesn't know anything about the objects it will be applied to.
34   *
35   * @author Rod Johnson
36   * @author Rob Harrop
37   * @author Juergen Hoeller
38   * @since 13 May 2001
39   * @see PropertyValues
40   * @see BeanWrapper
41   */
42  @SuppressWarnings("serial")
43  public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {
44  
45  	private final String name;
46  
47  	private final Object value;
48  
49  	private Object source;
50  
51  	private boolean optional = false;
52  
53  	private boolean converted = false;
54  
55  	private Object convertedValue;
56  
57  	/** Package-visible field that indicates whether conversion is necessary */
58  	volatile Boolean conversionNecessary;
59  
60  	/** Package-visible field for caching the resolved property path tokens */
61  	transient volatile Object resolvedTokens;
62  
63  	/** Package-visible field for caching the resolved PropertyDescriptor */
64  	transient volatile PropertyDescriptor resolvedDescriptor;
65  
66  
67  	/**
68  	 * Create a new PropertyValue instance.
69  	 * @param name the name of the property (never {@code null})
70  	 * @param value the value of the property (possibly before type conversion)
71  	 */
72  	public PropertyValue(String name, Object value) {
73  		this.name = name;
74  		this.value = value;
75  	}
76  
77  	/**
78  	 * Copy constructor.
79  	 * @param original the PropertyValue to copy (never {@code null})
80  	 */
81  	public PropertyValue(PropertyValue original) {
82  		Assert.notNull(original, "Original must not be null");
83  		this.name = original.getName();
84  		this.value = original.getValue();
85  		this.source = original.getSource();
86  		this.optional = original.isOptional();
87  		this.converted = original.converted;
88  		this.convertedValue = original.convertedValue;
89  		this.conversionNecessary = original.conversionNecessary;
90  		this.resolvedTokens = original.resolvedTokens;
91  		this.resolvedDescriptor = original.resolvedDescriptor;
92  		copyAttributesFrom(original);
93  	}
94  
95  	/**
96  	 * Constructor that exposes a new value for an original value holder.
97  	 * The original holder will be exposed as source of the new holder.
98  	 * @param original the PropertyValue to link to (never {@code null})
99  	 * @param newValue the new value to apply
100 	 */
101 	public PropertyValue(PropertyValue original, Object newValue) {
102 		Assert.notNull(original, "Original must not be null");
103 		this.name = original.getName();
104 		this.value = newValue;
105 		this.source = original;
106 		this.optional = original.isOptional();
107 		this.conversionNecessary = original.conversionNecessary;
108 		this.resolvedTokens = original.resolvedTokens;
109 		this.resolvedDescriptor = original.resolvedDescriptor;
110 		copyAttributesFrom(original);
111 	}
112 
113 
114 	/**
115 	 * Return the name of the property.
116 	 */
117 	public String getName() {
118 		return this.name;
119 	}
120 
121 	/**
122 	 * Return the value of the property.
123 	 * <p>Note that type conversion will <i>not</i> have occurred here.
124 	 * It is the responsibility of the BeanWrapper implementation to
125 	 * perform type conversion.
126 	 */
127 	public Object getValue() {
128 		return this.value;
129 	}
130 
131 	/**
132 	 * Return the original PropertyValue instance for this value holder.
133 	 * @return the original PropertyValue (either a source of this
134 	 * value holder or this value holder itself).
135 	 */
136 	public PropertyValue getOriginalPropertyValue() {
137 		PropertyValue original = this;
138 		while (original.source instanceof PropertyValue && original.source != original) {
139 			original = (PropertyValue) original.source;
140 		}
141 		return original;
142 	}
143 
144 	public void setOptional(boolean optional) {
145 		this.optional = optional;
146 	}
147 
148 	public boolean isOptional() {
149 		return this.optional;
150 	}
151 
152 	/**
153 	 * Return whether this holder contains a converted value already ({@code true}),
154 	 * or whether the value still needs to be converted ({@code false}).
155 	 */
156 	public synchronized boolean isConverted() {
157 		return this.converted;
158 	}
159 
160 	/**
161 	 * Set the converted value of the constructor argument,
162 	 * after processed type conversion.
163 	 */
164 	public synchronized void setConvertedValue(Object value) {
165 		this.converted = true;
166 		this.convertedValue = value;
167 	}
168 
169 	/**
170 	 * Return the converted value of the constructor argument,
171 	 * after processed type conversion.
172 	 */
173 	public synchronized Object getConvertedValue() {
174 		return this.convertedValue;
175 	}
176 
177 
178 	@Override
179 	public boolean equals(Object other) {
180 		if (this == other) {
181 			return true;
182 		}
183 		if (!(other instanceof PropertyValue)) {
184 			return false;
185 		}
186 		PropertyValue otherPv = (PropertyValue) other;
187 		return (this.name.equals(otherPv.name) &&
188 				ObjectUtils.nullSafeEquals(this.value, otherPv.value) &&
189 				ObjectUtils.nullSafeEquals(this.source, otherPv.source));
190 	}
191 
192 	@Override
193 	public int hashCode() {
194 		return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
195 	}
196 
197 	@Override
198 	public String toString() {
199 		return "bean property '" + this.name + "'";
200 	}
201 
202 }