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.io.Serializable;
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.springframework.util.StringUtils;
026:
027: /**
028: * Default implementation of the {@link PropertyValues} interface.
029: * Allows simple manipulation of properties, and provides constructors
030: * to support deep copy and construction from a Map.
031: *
032: * @author Rod Johnson
033: * @author Juergen Hoeller
034: * @author Rob Harrop
035: * @since 13 May 2001
036: */
037: public class MutablePropertyValues implements PropertyValues,
038: Serializable {
039:
040: /** List of PropertyValue objects */
041: private final List propertyValueList;
042:
043: private volatile boolean converted = false;
044:
045: /**
046: * Creates a new empty MutablePropertyValues object.
047: * Property values can be added with the <code>addPropertyValue</code> methods.
048: * @see #addPropertyValue(PropertyValue)
049: * @see #addPropertyValue(String, Object)
050: */
051: public MutablePropertyValues() {
052: this .propertyValueList = new ArrayList();
053: }
054:
055: /**
056: * Deep copy constructor. Guarantees PropertyValue references
057: * are independent, although it can't deep copy objects currently
058: * referenced by individual PropertyValue objects.
059: * @param original the PropertyValues to copy
060: * @see #addPropertyValues(PropertyValues)
061: */
062: public MutablePropertyValues(PropertyValues original) {
063: // We can optimize this because it's all new:
064: // There is no replacement of existing property values.
065: if (original != null) {
066: PropertyValue[] pvs = original.getPropertyValues();
067: this .propertyValueList = new ArrayList(pvs.length);
068: for (int i = 0; i < pvs.length; i++) {
069: PropertyValue newPv = new PropertyValue(pvs[i]);
070: this .propertyValueList.add(newPv);
071: }
072: } else {
073: this .propertyValueList = new ArrayList(0);
074: }
075: }
076:
077: /**
078: * Construct a new MutablePropertyValues object from a Map.
079: * @param original Map with property values keyed by property name Strings
080: * @see #addPropertyValues(Map)
081: */
082: public MutablePropertyValues(Map original) {
083: // We can optimize this because it's all new:
084: // There is no replacement of existing property values.
085: if (original != null) {
086: this .propertyValueList = new ArrayList(original.size());
087: Iterator it = original.entrySet().iterator();
088: while (it.hasNext()) {
089: Map.Entry entry = (Map.Entry) it.next();
090: PropertyValue newPv = new PropertyValue((String) entry
091: .getKey(), entry.getValue());
092: this .propertyValueList.add(newPv);
093: }
094: } else {
095: this .propertyValueList = new ArrayList(0);
096: }
097: }
098:
099: /**
100: * Construct a new MutablePropertyValues object using the given List of
101: * PropertyValue objects as-is.
102: * <p>This is a constructor for advanced usage scenarios.
103: * It is not intended for typical programmatic use.
104: * @param propertyValueList List of PropertyValue objects
105: */
106: public MutablePropertyValues(List propertyValueList) {
107: this .propertyValueList = (propertyValueList != null ? propertyValueList
108: : new ArrayList());
109: }
110:
111: /**
112: * Return the underlying List of PropertyValue objects in its raw form.
113: * The returned List can be modified directly, although this is not recommended.
114: * <p>This is an accessor for optimized access to all PropertyValue objects.
115: * It is not intended for typical programmatic use.
116: */
117: public List getPropertyValueList() {
118: return this .propertyValueList;
119: }
120:
121: /**
122: * Copy all given PropertyValues into this object. Guarantees PropertyValue
123: * references are independent, although it can't deep copy objects currently
124: * referenced by individual PropertyValue objects.
125: * @param other the PropertyValues to copy
126: * @return this object to allow creating objects, adding multiple PropertyValues
127: * in a single statement
128: */
129: public MutablePropertyValues addPropertyValues(PropertyValues other) {
130: if (other != null) {
131: PropertyValue[] pvs = other.getPropertyValues();
132: for (int i = 0; i < pvs.length; i++) {
133: PropertyValue newPv = new PropertyValue(pvs[i]);
134: addPropertyValue(newPv);
135: }
136: }
137: return this ;
138: }
139:
140: /**
141: * Add all property values from the given Map.
142: * @param other Map with property values keyed by property name,
143: * which must be a String
144: * @return this object to allow creating objects, adding multiple
145: * PropertyValues in a single statement
146: */
147: public MutablePropertyValues addPropertyValues(Map other) {
148: if (other != null) {
149: Iterator it = other.entrySet().iterator();
150: while (it.hasNext()) {
151: Map.Entry entry = (Map.Entry) it.next();
152: PropertyValue newPv = new PropertyValue((String) entry
153: .getKey(), entry.getValue());
154: addPropertyValue(newPv);
155: }
156: }
157: return this ;
158: }
159:
160: /**
161: * Add a PropertyValue object, replacing any existing one
162: * for the corresponding property.
163: * @param pv PropertyValue object to add
164: * @return this object to allow creating objects, adding multiple
165: * PropertyValues in a single statement
166: */
167: public MutablePropertyValues addPropertyValue(PropertyValue pv) {
168: for (int i = 0; i < this .propertyValueList.size(); i++) {
169: PropertyValue currentPv = (PropertyValue) this .propertyValueList
170: .get(i);
171: if (currentPv.getName().equals(pv.getName())) {
172: pv = mergeIfRequired(pv, currentPv);
173: setPropertyValueAt(pv, i);
174: return this ;
175: }
176: }
177: this .propertyValueList.add(pv);
178: return this ;
179: }
180:
181: /**
182: * Overloaded version of <code>addPropertyValue</code> that takes
183: * a property name and a property value.
184: * @param propertyName name of the property
185: * @param propertyValue value of the property
186: * @see #addPropertyValue(PropertyValue)
187: */
188: public void addPropertyValue(String propertyName,
189: Object propertyValue) {
190: addPropertyValue(new PropertyValue(propertyName, propertyValue));
191: }
192:
193: /**
194: * Modify a PropertyValue object held in this object.
195: * Indexed from 0.
196: */
197: public void setPropertyValueAt(PropertyValue pv, int i) {
198: this .propertyValueList.set(i, pv);
199: }
200:
201: /**
202: * Merges the value of the supplied 'new' {@link PropertyValue} with that of
203: * the current {@link PropertyValue} if merging is supported and enabled.
204: * @see Mergeable
205: */
206: private PropertyValue mergeIfRequired(PropertyValue newPv,
207: PropertyValue currentPv) {
208: Object value = newPv.getValue();
209: if (value instanceof Mergeable) {
210: Mergeable mergeable = (Mergeable) value;
211: if (mergeable.isMergeEnabled()) {
212: Object merged = mergeable.merge(currentPv.getValue());
213: return new PropertyValue(newPv.getName(), merged);
214: }
215: }
216: return newPv;
217: }
218:
219: /**
220: * Overloaded version of <code>removePropertyValue</code> that takes a property name.
221: * @param propertyName name of the property
222: * @see #removePropertyValue(PropertyValue)
223: */
224: public void removePropertyValue(String propertyName) {
225: removePropertyValue(getPropertyValue(propertyName));
226: }
227:
228: /**
229: * Remove the given PropertyValue, if contained.
230: * @param pv the PropertyValue to remove
231: */
232: public void removePropertyValue(PropertyValue pv) {
233: this .propertyValueList.remove(pv);
234: }
235:
236: /**
237: * Clear this holder, removing all PropertyValues.
238: */
239: public void clear() {
240: this .propertyValueList.clear();
241: }
242:
243: public PropertyValue[] getPropertyValues() {
244: return (PropertyValue[]) this .propertyValueList
245: .toArray(new PropertyValue[this .propertyValueList
246: .size()]);
247: }
248:
249: public PropertyValue getPropertyValue(String propertyName) {
250: for (int i = 0; i < this .propertyValueList.size(); i++) {
251: PropertyValue pv = (PropertyValue) this .propertyValueList
252: .get(i);
253: if (pv.getName().equals(propertyName)) {
254: return pv;
255: }
256: }
257: return null;
258: }
259:
260: public boolean contains(String propertyName) {
261: return (getPropertyValue(propertyName) != null);
262: }
263:
264: public boolean isEmpty() {
265: return this .propertyValueList.isEmpty();
266: }
267:
268: public int size() {
269: return this .propertyValueList.size();
270: }
271:
272: public PropertyValues changesSince(PropertyValues old) {
273: MutablePropertyValues changes = new MutablePropertyValues();
274: if (old == this ) {
275: return changes;
276: }
277:
278: // for each property value in the new set
279: for (Iterator it = this .propertyValueList.iterator(); it
280: .hasNext();) {
281: PropertyValue newPv = (PropertyValue) it.next();
282: // if there wasn't an old one, add it
283: PropertyValue pvOld = old.getPropertyValue(newPv.getName());
284: if (pvOld == null) {
285: changes.addPropertyValue(newPv);
286: } else if (!pvOld.equals(newPv)) {
287: // it's changed
288: changes.addPropertyValue(newPv);
289: }
290: }
291: return changes;
292: }
293:
294: /**
295: * Mark this holder as containing converted values only
296: * (i.e. no runtime resolution needed anymore).
297: */
298: public void setConverted() {
299: this .converted = true;
300: }
301:
302: /**
303: * Return whether this holder contains converted values only (<code>true</code>),
304: * or whether the values still need to be converted (<code>false</code>).
305: */
306: public boolean isConverted() {
307: return this .converted;
308: }
309:
310: public boolean equals(Object other) {
311: if (this == other) {
312: return true;
313: }
314: if (!(other instanceof MutablePropertyValues)) {
315: return false;
316: }
317: MutablePropertyValues that = (MutablePropertyValues) other;
318: return this .propertyValueList.equals(that.propertyValueList);
319: }
320:
321: public int hashCode() {
322: return this .propertyValueList.hashCode();
323: }
324:
325: public String toString() {
326: PropertyValue[] pvs = getPropertyValues();
327: StringBuffer sb = new StringBuffer("PropertyValues: length="
328: + pvs.length + "; ");
329: sb.append(StringUtils.arrayToDelimitedString(pvs, "; "));
330: return sb.toString();
331: }
332:
333: }
|