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.factory.config;
018:
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Set;
026:
027: import org.springframework.beans.BeanMetadataElement;
028: import org.springframework.beans.Mergeable;
029: import org.springframework.util.Assert;
030: import org.springframework.util.ClassUtils;
031: import org.springframework.util.ObjectUtils;
032:
033: /**
034: * Holder for constructor argument values, typically as part of a bean definition.
035: *
036: * <p>Supports values for a specific index in the constructor argument list
037: * as well as for generic argument matches by type.
038: *
039: * @author Juergen Hoeller
040: * @since 09.11.2003
041: * @see BeanDefinition#getConstructorArgumentValues
042: */
043: public class ConstructorArgumentValues {
044:
045: private final Map indexedArgumentValues = new HashMap();
046:
047: private final List genericArgumentValues = new LinkedList();
048:
049: /**
050: * Create a new empty ConstructorArgumentValues object.
051: */
052: public ConstructorArgumentValues() {
053: }
054:
055: /**
056: * Deep copy constructor.
057: * @param original the ConstructorArgumentValues to copy
058: */
059: public ConstructorArgumentValues(ConstructorArgumentValues original) {
060: addArgumentValues(original);
061: }
062:
063: /**
064: * Copy all given argument values into this object, using separate holder
065: * instances to keep the values independent from the original object.
066: * <p>Note: Identical ValueHolder instances will only be registered once,
067: * to allow for merging and re-merging of argument value definitions. Distinct
068: * ValueHolder instances carrying the same content are of course allowed.
069: */
070: public void addArgumentValues(ConstructorArgumentValues other) {
071: if (other != null) {
072: for (Iterator it = other.indexedArgumentValues.entrySet()
073: .iterator(); it.hasNext();) {
074: Map.Entry entry = (Map.Entry) it.next();
075: ValueHolder valueHolder = (ValueHolder) entry
076: .getValue();
077: addOrMergeIndexedArgumentValue(entry.getKey(),
078: valueHolder.copy());
079: }
080: for (Iterator it = other.genericArgumentValues.iterator(); it
081: .hasNext();) {
082: ValueHolder valueHolder = (ValueHolder) it.next();
083: if (!this .genericArgumentValues.contains(valueHolder)) {
084: this .genericArgumentValues.add(valueHolder.copy());
085: }
086: }
087: }
088: }
089:
090: /**
091: * Add argument value for the given index in the constructor argument list.
092: * @param index the index in the constructor argument list
093: * @param value the argument value
094: */
095: public void addIndexedArgumentValue(int index, Object value) {
096: addIndexedArgumentValue(index, new ValueHolder(value));
097: }
098:
099: /**
100: * Add argument value for the given index in the constructor argument list.
101: * @param index the index in the constructor argument list
102: * @param value the argument value
103: * @param type the type of the constructor argument
104: */
105: public void addIndexedArgumentValue(int index, Object value,
106: String type) {
107: addIndexedArgumentValue(index, new ValueHolder(value, type));
108: }
109:
110: /**
111: * Add argument value for the given index in the constructor argument list.
112: * @param index the index in the constructor argument list
113: * @param newValue the argument value in the form of a ValueHolder
114: */
115: public void addIndexedArgumentValue(int index, ValueHolder newValue) {
116: Assert.isTrue(index >= 0, "Index must not be negative");
117: Assert.notNull(newValue, "ValueHolder must not be null");
118: addOrMergeIndexedArgumentValue(new Integer(index), newValue);
119: }
120:
121: /**
122: * Add argument value for the given index in the constructor argument list,
123: * merging the new value (typically a collection) with the current value
124: * if demanded: see {@link org.springframework.beans.Mergeable}.
125: * @param key the index in the constructor argument list
126: * @param newValue the argument value in the form of a ValueHolder
127: */
128: private void addOrMergeIndexedArgumentValue(Object key,
129: ValueHolder newValue) {
130: ValueHolder currentValue = (ValueHolder) this .indexedArgumentValues
131: .get(key);
132: if (currentValue != null
133: && newValue.getValue() instanceof Mergeable) {
134: Mergeable mergeable = (Mergeable) newValue.getValue();
135: if (mergeable.isMergeEnabled()) {
136: newValue.setValue(mergeable.merge(currentValue
137: .getValue()));
138: }
139: }
140: this .indexedArgumentValues.put(key, newValue);
141: }
142:
143: /**
144: * Get argument value for the given index in the constructor argument list.
145: * @param index the index in the constructor argument list
146: * @param requiredType the type to match (can be <code>null</code> to match
147: * untyped values only)
148: * @return the ValueHolder for the argument, or <code>null</code> if none set
149: */
150: public ValueHolder getIndexedArgumentValue(int index,
151: Class requiredType) {
152: Assert.isTrue(index >= 0, "Index must not be negative");
153: ValueHolder valueHolder = (ValueHolder) this .indexedArgumentValues
154: .get(new Integer(index));
155: if (valueHolder != null) {
156: if (valueHolder.getType() == null
157: || (requiredType != null && requiredType.getName()
158: .equals(valueHolder.getType()))) {
159: return valueHolder;
160: }
161: }
162: return null;
163: }
164:
165: /**
166: * Return the map of indexed argument values.
167: * @return unmodifiable Map with Integer index as key and ValueHolder as value
168: * @see ValueHolder
169: */
170: public Map getIndexedArgumentValues() {
171: return Collections.unmodifiableMap(this .indexedArgumentValues);
172: }
173:
174: /**
175: * Add generic argument value to be matched by type.
176: * <p>Note: A single generic argument value will just be used once,
177: * rather than matched multiple times (as of Spring 1.1).
178: * @param value the argument value
179: */
180: public void addGenericArgumentValue(Object value) {
181: this .genericArgumentValues.add(new ValueHolder(value));
182: }
183:
184: /**
185: * Add generic argument value to be matched by type.
186: * <p>Note: A single generic argument value will just be used once,
187: * rather than matched multiple times (as of Spring 1.1).
188: * @param value the argument value
189: * @param type the type of the constructor argument
190: */
191: public void addGenericArgumentValue(Object value, String type) {
192: this .genericArgumentValues.add(new ValueHolder(value, type));
193: }
194:
195: /**
196: * Add generic argument value to be matched by type.
197: * <p>Note: A single generic argument value will just be used once,
198: * rather than matched multiple times (as of Spring 1.1).
199: * @param newValue the argument value in the form of a ValueHolder
200: * <p>Note: Identical ValueHolder instances will only be registered once,
201: * to allow for merging and re-merging of argument value definitions. Distinct
202: * ValueHolder instances carrying the same content are of course allowed.
203: */
204: public void addGenericArgumentValue(ValueHolder newValue) {
205: Assert.notNull(newValue, "ValueHolder must not be null");
206: if (!this .genericArgumentValues.contains(newValue)) {
207: this .genericArgumentValues.add(newValue);
208: }
209: }
210:
211: /**
212: * Look for a generic argument value that matches the given type.
213: * @param requiredType the type to match (can be <code>null</code> to find
214: * an arbitrary next generic argument value)
215: * @return the ValueHolder for the argument, or <code>null</code> if none set
216: */
217: public ValueHolder getGenericArgumentValue(Class requiredType) {
218: return getGenericArgumentValue(requiredType, null);
219: }
220:
221: /**
222: * Look for the next generic argument value that matches the given type,
223: * ignoring argument values that have already been used in the current
224: * resolution process.
225: * @param requiredType the type to match (can be <code>null</code> to find
226: * an arbitrary next generic argument value)
227: * @param usedValueHolders a Set of ValueHolder objects that have already been used
228: * in the current resolution process and should therefore not be returned again
229: * @return the ValueHolder for the argument, or <code>null</code> if none found
230: */
231: public ValueHolder getGenericArgumentValue(Class requiredType,
232: Set usedValueHolders) {
233: for (Iterator it = this .genericArgumentValues.iterator(); it
234: .hasNext();) {
235: ValueHolder valueHolder = (ValueHolder) it.next();
236: if (usedValueHolders == null
237: || !usedValueHolders.contains(valueHolder)) {
238: if (requiredType != null) {
239: // Check matching type.
240: if (valueHolder.getType() != null) {
241: if (valueHolder.getType().equals(
242: requiredType.getName())) {
243: return valueHolder;
244: }
245: } else if (ClassUtils.isAssignableValue(
246: requiredType, valueHolder.getValue())) {
247: return valueHolder;
248: }
249: } else {
250: // No required type specified -> consider untyped values only.
251: if (valueHolder.getType() == null) {
252: return valueHolder;
253: }
254: }
255: }
256: }
257: return null;
258: }
259:
260: /**
261: * Return the list of generic argument values.
262: * @return unmodifiable List of ValueHolders
263: * @see ValueHolder
264: */
265: public List getGenericArgumentValues() {
266: return Collections.unmodifiableList(this .genericArgumentValues);
267: }
268:
269: /**
270: * Look for an argument value that either corresponds to the given index
271: * in the constructor argument list or generically matches by type.
272: * @param index the index in the constructor argument list
273: * @param requiredType the type to match
274: * @return the ValueHolder for the argument, or <code>null</code> if none set
275: */
276: public ValueHolder getArgumentValue(int index, Class requiredType) {
277: return getArgumentValue(index, requiredType, null);
278: }
279:
280: /**
281: * Look for an argument value that either corresponds to the given index
282: * in the constructor argument list or generically matches by type.
283: * @param index the index in the constructor argument list
284: * @param requiredType the type to match (can be <code>null</code> to find
285: * an untyped argument value)
286: * @param usedValueHolders a Set of ValueHolder objects that have already
287: * been used in the current resolution process and should therefore not
288: * be returned again (allowing to return the next generic argument match
289: * in case of multiple generic argument values of the same type)
290: * @return the ValueHolder for the argument, or <code>null</code> if none set
291: */
292: public ValueHolder getArgumentValue(int index, Class requiredType,
293: Set usedValueHolders) {
294: Assert.isTrue(index >= 0, "Index must not be negative");
295: ValueHolder valueHolder = getIndexedArgumentValue(index,
296: requiredType);
297: if (valueHolder == null) {
298: valueHolder = getGenericArgumentValue(requiredType,
299: usedValueHolders);
300: }
301: return valueHolder;
302: }
303:
304: /**
305: * Return the number of argument values held in this instance,
306: * counting both indexed and generic argument values.
307: */
308: public int getArgumentCount() {
309: return (this .indexedArgumentValues.size() + this .genericArgumentValues
310: .size());
311: }
312:
313: /**
314: * Return if this holder does not contain any argument values,
315: * neither indexed ones nor generic ones.
316: */
317: public boolean isEmpty() {
318: return (this .indexedArgumentValues.isEmpty() && this .genericArgumentValues
319: .isEmpty());
320: }
321:
322: /**
323: * Clear this holder, removing all argument values.
324: */
325: public void clear() {
326: this .indexedArgumentValues.clear();
327: this .genericArgumentValues.clear();
328: }
329:
330: public boolean equals(Object other) {
331: if (this == other) {
332: return true;
333: }
334: if (!(other instanceof ConstructorArgumentValues)) {
335: return false;
336: }
337: ConstructorArgumentValues that = (ConstructorArgumentValues) other;
338: if (this .genericArgumentValues.size() != that.genericArgumentValues
339: .size()
340: || this .indexedArgumentValues.size() != that.indexedArgumentValues
341: .size()) {
342: return false;
343: }
344: Iterator it1 = this .genericArgumentValues.iterator();
345: Iterator it2 = that.genericArgumentValues.iterator();
346: while (it1.hasNext() && it2.hasNext()) {
347: ValueHolder vh1 = (ValueHolder) it1.next();
348: ValueHolder vh2 = (ValueHolder) it2.next();
349: if (!vh1.contentEquals(vh2)) {
350: return false;
351: }
352: }
353: for (Iterator it = this .indexedArgumentValues.entrySet()
354: .iterator(); it.hasNext();) {
355: Map.Entry entry = (Map.Entry) it.next();
356: ValueHolder vh1 = (ValueHolder) entry.getValue();
357: ValueHolder vh2 = (ValueHolder) that.indexedArgumentValues
358: .get(entry.getKey());
359: if (!vh1.contentEquals(vh2)) {
360: return false;
361: }
362: }
363: return true;
364: }
365:
366: public int hashCode() {
367: int hashCode = 7;
368: for (Iterator it = this .genericArgumentValues.iterator(); it
369: .hasNext();) {
370: ValueHolder valueHolder = (ValueHolder) it.next();
371: hashCode = 31 * hashCode + valueHolder.contentHashCode();
372: }
373: hashCode = 29 * hashCode;
374: for (Iterator it = this .indexedArgumentValues.entrySet()
375: .iterator(); it.hasNext();) {
376: Map.Entry entry = (Map.Entry) it.next();
377: Integer key = (Integer) entry.getKey();
378: ValueHolder value = (ValueHolder) entry.getValue();
379: hashCode = 31 * hashCode
380: + (value.contentHashCode() ^ key.hashCode());
381: }
382: return hashCode;
383: }
384:
385: /**
386: * Holder for a constructor argument value, with an optional type
387: * attribute indicating the target type of the actual constructor argument.
388: */
389: public static class ValueHolder implements BeanMetadataElement {
390:
391: private Object value;
392:
393: private String type;
394:
395: private Object source;
396:
397: private boolean converted = false;
398:
399: private Object convertedValue;
400:
401: /**
402: * Create a new ValueHolder for the given value.
403: * @param value the argument value
404: */
405: public ValueHolder(Object value) {
406: this .value = value;
407: }
408:
409: /**
410: * Create a new ValueHolder for the given value and type.
411: * @param value the argument value
412: * @param type the type of the constructor argument
413: */
414: public ValueHolder(Object value, String type) {
415: this .value = value;
416: this .type = type;
417: }
418:
419: /**
420: * Set the value for the constructor argument.
421: * Only necessary for manipulating a registered value,
422: * for example in BeanFactoryPostProcessors.
423: * @see PropertyPlaceholderConfigurer
424: */
425: public void setValue(Object value) {
426: this .value = value;
427: }
428:
429: /**
430: * Return the value for the constructor argument.
431: */
432: public Object getValue() {
433: return this .value;
434: }
435:
436: /**
437: * Set the type of the constructor argument.
438: * Only necessary for manipulating a registered value,
439: * for example in BeanFactoryPostProcessors.
440: * @see PropertyPlaceholderConfigurer
441: */
442: public void setType(String type) {
443: this .type = type;
444: }
445:
446: /**
447: * Return the type of the constructor argument.
448: */
449: public String getType() {
450: return this .type;
451: }
452:
453: /**
454: * Set the configuration source <code>Object</code> for this metadata element.
455: * <p>The exact type of the object will depend on the configuration mechanism used.
456: */
457: public void setSource(Object source) {
458: this .source = source;
459: }
460:
461: public Object getSource() {
462: return this .source;
463: }
464:
465: /**
466: * Return whether this holder contains a converted value already (<code>true</code>),
467: * or whether the value still needs to be converted (<code>false</code>).
468: */
469: public synchronized boolean isConverted() {
470: return this .converted;
471: }
472:
473: /**
474: * Set the converted value of the constructor argument,
475: * after processed type conversion.
476: */
477: public synchronized void setConvertedValue(Object value) {
478: this .converted = true;
479: this .convertedValue = value;
480: }
481:
482: /**
483: * Return the converted value of the constructor argument,
484: * after processed type conversion.
485: */
486: public synchronized Object getConvertedValue() {
487: return this .convertedValue;
488: }
489:
490: /**
491: * Determine whether the content of this ValueHolder is equal
492: * to the content of the given other ValueHolder.
493: * <p>Note that ValueHolder does not implement <code>equals</code>
494: * directly, to allow for multiple ValueHolder instances with the
495: * same content to reside in the same Set.
496: */
497: private boolean contentEquals(ValueHolder other) {
498: return (this == other || (ObjectUtils.nullSafeEquals(
499: this .value, other.value) && ObjectUtils
500: .nullSafeEquals(this .type, other.type)));
501: }
502:
503: /**
504: * Determine whether the hash code of the content of this ValueHolder.
505: * <p>Note that ValueHolder does not implement <code>hashCode</code>
506: * directly, to allow for multiple ValueHolder instances with the
507: * same content to reside in the same Set.
508: */
509: private int contentHashCode() {
510: return ObjectUtils.nullSafeHashCode(this .value) * 29
511: + ObjectUtils.nullSafeHashCode(this .type);
512: }
513:
514: /**
515: * Create a copy of this ValueHolder: that is, an independent
516: * ValueHolder instance with the same contents.
517: */
518: public ValueHolder copy() {
519: ValueHolder copy = new ValueHolder(this.value, this.type);
520: copy.setSource(this.source);
521: return copy;
522: }
523: }
524:
525: }
|