ConstructorArgumentValues.java 19.4 KB
Newer Older
1
/*
2
 * Copyright 2002-2010 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.config;

import java.util.Collections;
import java.util.Iterator;
21
import java.util.LinkedHashMap;
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
 * Holder for constructor argument values, typically as part of a bean definition.
 *
 * <p>Supports values for a specific index in the constructor argument list
 * as well as for generic argument matches by type.
 *
 * @author Juergen Hoeller
 * @since 09.11.2003
 * @see BeanDefinition#getConstructorArgumentValues
 */
public class ConstructorArgumentValues {

45
	private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<Integer, ValueHolder>(0);
46

47
	private final List<ValueHolder> genericArgumentValues = new LinkedList<ValueHolder>();
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63


	/**
	 * Create a new empty ConstructorArgumentValues object.
	 */
	public ConstructorArgumentValues() {
	}

	/**
	 * Deep copy constructor.
	 * @param original the ConstructorArgumentValues to copy
	 */
	public ConstructorArgumentValues(ConstructorArgumentValues original) {
		addArgumentValues(original);
	}

64

65 66 67 68 69 70 71 72 73
	/**
	 * Copy all given argument values into this object, using separate holder
	 * instances to keep the values independent from the original object.
	 * <p>Note: Identical ValueHolder instances will only be registered once,
	 * to allow for merging and re-merging of argument value definitions. Distinct
	 * ValueHolder instances carrying the same content are of course allowed.
	 */
	public void addArgumentValues(ConstructorArgumentValues other) {
		if (other != null) {
74 75
			for (Map.Entry<Integer, ValueHolder> entry : other.indexedArgumentValues.entrySet()) {
				addOrMergeIndexedArgumentValue(entry.getKey(), entry.getValue().copy());
76
			}
77
			for (ValueHolder valueHolder : other.genericArgumentValues) {
78
				if (!this.genericArgumentValues.contains(valueHolder)) {
79
					addOrMergeGenericArgumentValue(valueHolder.copy());
80 81 82 83 84 85 86
				}
			}
		}
	}


	/**
87
	 * Add an argument value for the given index in the constructor argument list.
88 89 90 91 92 93 94 95
	 * @param index the index in the constructor argument list
	 * @param value the argument value
	 */
	public void addIndexedArgumentValue(int index, Object value) {
		addIndexedArgumentValue(index, new ValueHolder(value));
	}

	/**
96
	 * Add an argument value for the given index in the constructor argument list.
97 98 99 100 101 102 103 104 105
	 * @param index the index in the constructor argument list
	 * @param value the argument value
	 * @param type the type of the constructor argument
	 */
	public void addIndexedArgumentValue(int index, Object value, String type) {
		addIndexedArgumentValue(index, new ValueHolder(value, type));
	}

	/**
106
	 * Add an argument value for the given index in the constructor argument list.
107 108 109 110 111 112
	 * @param index the index in the constructor argument list
	 * @param newValue the argument value in the form of a ValueHolder
	 */
	public void addIndexedArgumentValue(int index, ValueHolder newValue) {
		Assert.isTrue(index >= 0, "Index must not be negative");
		Assert.notNull(newValue, "ValueHolder must not be null");
113
		addOrMergeIndexedArgumentValue(index, newValue);
114 115 116
	}

	/**
117
	 * Add an argument value for the given index in the constructor argument list,
118 119 120 121 122
	 * merging the new value (typically a collection) with the current value
	 * if demanded: see {@link org.springframework.beans.Mergeable}.
	 * @param key the index in the constructor argument list
	 * @param newValue the argument value in the form of a ValueHolder
	 */
123 124
	private void addOrMergeIndexedArgumentValue(Integer key, ValueHolder newValue) {
		ValueHolder currentValue = this.indexedArgumentValues.get(key);
125 126 127 128 129 130 131 132 133
		if (currentValue != null && newValue.getValue() instanceof Mergeable) {
			Mergeable mergeable = (Mergeable) newValue.getValue();
			if (mergeable.isMergeEnabled()) {
				newValue.setValue(mergeable.merge(currentValue.getValue()));
			}
		}
		this.indexedArgumentValues.put(key, newValue);
	}

134 135 136 137 138 139 140 141
	/**
	 * Check whether an argument value has been registered for the given index.
	 * @param index the index in the constructor argument list
	 */
	public boolean hasIndexedArgumentValue(int index) {
		return this.indexedArgumentValues.containsKey(index);
	}

142 143 144 145 146 147 148 149
	/**
	 * Get argument value for the given index in the constructor argument list.
	 * @param index the index in the constructor argument list
	 * @param requiredType the type to match (can be <code>null</code> to match
	 * untyped values only)
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
150 151 152 153 154 155 156 157 158 159 160 161 162
		return getIndexedArgumentValue(index, requiredType, null);
	}

	/**
	 * Get argument value for the given index in the constructor argument list.
	 * @param index the index in the constructor argument list
	 * @param requiredType the type to match (can be <code>null</code> to match
	 * untyped values only)
	 * @param requiredName the type to match (can be <code>null</code> to match
	 * unnamed values only)
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getIndexedArgumentValue(int index, Class requiredType, String requiredName) {
163
		Assert.isTrue(index >= 0, "Index must not be negative");
164
		ValueHolder valueHolder = this.indexedArgumentValues.get(index);
165 166
		if (valueHolder != null &&
				(valueHolder.getType() == null ||
167
						(requiredType != null && ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) &&
168 169 170
				(valueHolder.getName() == null ||
						(requiredName != null && requiredName.equals(valueHolder.getName())))) {
			return valueHolder;
171 172 173 174 175 176 177 178 179
		}
		return null;
	}

	/**
	 * Return the map of indexed argument values.
	 * @return unmodifiable Map with Integer index as key and ValueHolder as value
	 * @see ValueHolder
	 */
180
	public Map<Integer, ValueHolder> getIndexedArgumentValues() {
181 182 183 184 185
		return Collections.unmodifiableMap(this.indexedArgumentValues);
	}


	/**
186
	 * Add a generic argument value to be matched by type.
187
	 * <p>Note: A single generic argument value will just be used once,
188
	 * rather than matched multiple times.
189 190 191 192 193 194 195
	 * @param value the argument value
	 */
	public void addGenericArgumentValue(Object value) {
		this.genericArgumentValues.add(new ValueHolder(value));
	}

	/**
196
	 * Add a 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.
199 200 201 202 203 204 205 206
	 * @param value the argument value
	 * @param type the type of the constructor argument
	 */
	public void addGenericArgumentValue(Object value, String type) {
		this.genericArgumentValues.add(new ValueHolder(value, type));
	}

	/**
207
	 * Add a generic argument value to be matched by type or name (if available).
208
	 * <p>Note: A single generic argument value will just be used once,
209
	 * rather than matched multiple times.
210 211 212 213 214 215 216 217
	 * @param newValue the argument value in the form of a ValueHolder
	 * <p>Note: Identical ValueHolder instances will only be registered once,
	 * to allow for merging and re-merging of argument value definitions. Distinct
	 * ValueHolder instances carrying the same content are of course allowed.
	 */
	public void addGenericArgumentValue(ValueHolder newValue) {
		Assert.notNull(newValue, "ValueHolder must not be null");
		if (!this.genericArgumentValues.contains(newValue)) {
218
			addOrMergeGenericArgumentValue(newValue);
219 220 221
		}
	}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
	/**
	 * Add a generic argument value, merging the new value (typically a collection)
	 * with the current value if demanded: see {@link org.springframework.beans.Mergeable}.
	 * @param newValue the argument value in the form of a ValueHolder
	 */
	private void addOrMergeGenericArgumentValue(ValueHolder newValue) {
		if (newValue.getName() != null) {
			for (Iterator<ValueHolder> it = this.genericArgumentValues.iterator(); it.hasNext();) {
				ValueHolder currentValue = it.next();
				if (newValue.getName().equals(currentValue.getName())) {
					if (newValue.getValue() instanceof Mergeable) {
						Mergeable mergeable = (Mergeable) newValue.getValue();
						if (mergeable.isMergeEnabled()) {
							newValue.setValue(mergeable.merge(currentValue.getValue()));
						}
					}
					it.remove();
				}
			}
		}
		this.genericArgumentValues.add(newValue);
	}

245 246
	/**
	 * Look for a generic argument value that matches the given type.
247
	 * @param requiredType the type to match
248 249 250
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getGenericArgumentValue(Class requiredType) {
251 252 253 254 255 256 257 258 259 260 261
		return getGenericArgumentValue(requiredType, null, null);
	}

	/**
	 * Look for a generic argument value that matches the given type.
	 * @param requiredType the type to match
	 * @param requiredName the name to match
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName) {
		return getGenericArgumentValue(requiredType, requiredName, null);
262 263 264 265 266 267 268 269
	}

	/**
	 * Look for the next generic argument value that matches the given type,
	 * ignoring argument values that have already been used in the current
	 * resolution process.
	 * @param requiredType the type to match (can be <code>null</code> to find
	 * an arbitrary next generic argument value)
270 271
	 * @param requiredName the name to match (can be <code>null</code> to not
	 * match argument values by name)
272 273 274 275
	 * @param usedValueHolders a Set of ValueHolder objects that have already been used
	 * in the current resolution process and should therefore not be returned again
	 * @return the ValueHolder for the argument, or <code>null</code> if none found
	 */
276
	public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
277
		for (ValueHolder valueHolder : this.genericArgumentValues) {
278 279 280
			if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) {
				continue;
			}
281 282
			if (valueHolder.getName() != null &&
					(requiredName == null || !valueHolder.getName().equals(requiredName))) {
283
				continue;
284
			}
285 286
			if (valueHolder.getType() != null &&
					(requiredType == null || !ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) {
287 288 289 290 291 292 293
				continue;
			}
			if (requiredType != null && valueHolder.getType() == null && valueHolder.getName() == null &&
					!ClassUtils.isAssignableValue(requiredType, valueHolder.getValue())) {
				continue;
			}
			return valueHolder;
294 295 296 297 298 299 300 301 302
		}
		return null;
	}

	/**
	 * Return the list of generic argument values.
	 * @return unmodifiable List of ValueHolders
	 * @see ValueHolder
	 */
303
	public List<ValueHolder> getGenericArgumentValues() {
304 305 306 307 308 309 310 311 312 313 314 315
		return Collections.unmodifiableList(this.genericArgumentValues);
	}


	/**
	 * Look for an argument value that either corresponds to the given index
	 * in the constructor argument list or generically matches by type.
	 * @param index the index in the constructor argument list
	 * @param requiredType the type to match
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getArgumentValue(int index, Class requiredType) {
316 317 318 319 320 321 322 323 324 325 326 327 328
		return getArgumentValue(index, requiredType, null, null);
	}

	/**
	 * Look for an argument value that either corresponds to the given index
	 * in the constructor argument list or generically matches by type.
	 * @param index the index in the constructor argument list
	 * @param requiredType the type to match
	 * @param requiredName the name to match
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
	public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName) {
		return getArgumentValue(index, requiredType, requiredName, null);
329 330 331 332 333 334 335 336 337 338 339 340 341 342
	}

	/**
	 * Look for an argument value that either corresponds to the given index
	 * in the constructor argument list or generically matches by type.
	 * @param index the index in the constructor argument list
	 * @param requiredType the type to match (can be <code>null</code> to find
	 * an untyped argument value)
	 * @param usedValueHolders a Set of ValueHolder objects that have already
	 * been used in the current resolution process and should therefore not
	 * be returned again (allowing to return the next generic argument match
	 * in case of multiple generic argument values of the same type)
	 * @return the ValueHolder for the argument, or <code>null</code> if none set
	 */
343
	public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
344
		Assert.isTrue(index >= 0, "Index must not be negative");
345
		ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
346
		if (valueHolder == null) {
347
			valueHolder = getGenericArgumentValue(requiredType, requiredName, usedValueHolders);
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
		}
		return valueHolder;
	}

	/**
	 * Return the number of argument values held in this instance,
	 * counting both indexed and generic argument values.
	 */
	public int getArgumentCount() {
		return (this.indexedArgumentValues.size() + this.genericArgumentValues.size());
	}

	/**
	 * Return if this holder does not contain any argument values,
	 * neither indexed ones nor generic ones.
	 */
	public boolean isEmpty() {
		return (this.indexedArgumentValues.isEmpty() && this.genericArgumentValues.isEmpty());
	}

	/**
	 * Clear this holder, removing all argument values.
	 */
	public void clear() {
		this.indexedArgumentValues.clear();
		this.genericArgumentValues.clear();
	}


377
	@Override
378 379 380 381 382 383 384 385 386 387 388 389
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof ConstructorArgumentValues)) {
			return false;
		}
		ConstructorArgumentValues that = (ConstructorArgumentValues) other;
		if (this.genericArgumentValues.size() != that.genericArgumentValues.size() ||
				this.indexedArgumentValues.size() != that.indexedArgumentValues.size()) {
			return false;
		}
J
Juergen Hoeller 已提交
390 391
		Iterator<ValueHolder> it1 = this.genericArgumentValues.iterator();
		Iterator<ValueHolder> it2 = that.genericArgumentValues.iterator();
392
		while (it1.hasNext() && it2.hasNext()) {
J
Juergen Hoeller 已提交
393 394
			ValueHolder vh1 = it1.next();
			ValueHolder vh2 = it2.next();
395 396 397 398
			if (!vh1.contentEquals(vh2)) {
				return false;
			}
		}
399 400 401
		for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) {
			ValueHolder vh1 = entry.getValue();
			ValueHolder vh2 = that.indexedArgumentValues.get(entry.getKey());
402 403 404 405 406 407 408
			if (!vh1.contentEquals(vh2)) {
				return false;
			}
		}
		return true;
	}

409
	@Override
410 411
	public int hashCode() {
		int hashCode = 7;
412
		for (ValueHolder valueHolder : this.genericArgumentValues) {
413 414 415
			hashCode = 31 * hashCode + valueHolder.contentHashCode();
		}
		hashCode = 29 * hashCode;
416 417
		for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) {
			hashCode = 31 * hashCode + (entry.getValue().contentHashCode() ^ entry.getKey().hashCode());
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
		}
		return hashCode;
	}


	/**
	 * Holder for a constructor argument value, with an optional type
	 * attribute indicating the target type of the actual constructor argument.
	 */
	public static class ValueHolder implements BeanMetadataElement {

		private Object value;

		private String type;

433 434
		private String name;

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
		private Object source;

		private boolean converted = false;

		private Object convertedValue;

		/**
		 * Create a new ValueHolder for the given value.
		 * @param value the argument value
		 */
		public ValueHolder(Object value) {
			this.value = value;
		}

		/**
		 * Create a new ValueHolder for the given value and type.
		 * @param value the argument value
		 * @param type the type of the constructor argument
		 */
		public ValueHolder(Object value, String type) {
			this.value = value;
			this.type = type;
		}

459 460 461 462 463 464 465 466 467 468 469 470
		/**
		 * Create a new ValueHolder for the given value, type and name.
		 * @param value the argument value
		 * @param type the type of the constructor argument
		 * @param name the name of the constructor argument
		 */
		public ValueHolder(Object value, String type, String name) {
			this.value = value;
			this.type = type;
			this.name = name;
		}

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
		/**
		 * Set the value for the constructor argument.
		 * @see PropertyPlaceholderConfigurer
		 */
		public void setValue(Object value) {
			this.value = value;
		}

		/**
		 * Return the value for the constructor argument.
		 */
		public Object getValue() {
			return this.value;
		}

		/**
		 * Set the type of the constructor argument.
		 */
		public void setType(String type) {
			this.type = type;
		}

		/**
		 * Return the type of the constructor argument.
		 */
		public String getType() {
			return this.type;
		}

500 501 502 503 504 505 506 507 508 509 510 511 512 513
		/**
		 * Set the name of the constructor argument.
		 */
		public void setName(String name) {
			this.name = name;
		}

		/**
		 * Return the name of the constructor argument.
		 */
		public String getName() {
			return this.name;
		}

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
		/**
		 * Set the configuration source <code>Object</code> for this metadata element.
		 * <p>The exact type of the object will depend on the configuration mechanism used.
		 */
		public void setSource(Object source) {
			this.source = source;
		}

		public Object getSource() {
			return this.source;
		}

		/**
		 * Return whether this holder contains a converted value already (<code>true</code>),
		 * or whether the value still needs to be converted (<code>false</code>).
		 */
		public synchronized boolean isConverted() {
			return this.converted;
		}

		/**
		 * Set the converted value of the constructor argument,
		 * after processed type conversion.
		 */
		public synchronized void setConvertedValue(Object value) {
			this.converted = true;
			this.convertedValue = value;
		}

		/**
		 * Return the converted value of the constructor argument,
		 * after processed type conversion.
		 */
		public synchronized Object getConvertedValue() {
			return this.convertedValue;
		}

		/**
		 * Determine whether the content of this ValueHolder is equal
		 * to the content of the given other ValueHolder.
		 * <p>Note that ValueHolder does not implement <code>equals</code>
		 * directly, to allow for multiple ValueHolder instances with the
		 * same content to reside in the same Set.
		 */
		private boolean contentEquals(ValueHolder other) {
			return (this == other ||
					(ObjectUtils.nullSafeEquals(this.value, other.value) && ObjectUtils.nullSafeEquals(this.type, other.type)));
		}

		/**
		 * Determine whether the hash code of the content of this ValueHolder.
		 * <p>Note that ValueHolder does not implement <code>hashCode</code>
		 * directly, to allow for multiple ValueHolder instances with the
		 * same content to reside in the same Set.
		 */
		private int contentHashCode() {
			return ObjectUtils.nullSafeHashCode(this.value) * 29 + ObjectUtils.nullSafeHashCode(this.type);
		}

		/**
		 * Create a copy of this ValueHolder: that is, an independent
		 * ValueHolder instance with the same contents.
		 */
		public ValueHolder copy() {
578
			ValueHolder copy = new ValueHolder(this.value, this.type, this.name);
579 580 581 582 583 584
			copy.setSource(this.source);
			return copy;
		}
	}

}