/*
 * Copyright 2002-2020 the original author or authors.
 *
 * 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
 *
 *      https://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.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
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.lang.Nullable;
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.

Supports values for a specific index in the constructor argument list as well as for generic argument matches by type.

Author:Juergen Hoeller
See Also:
Since:09.11.2003
/** * 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 { private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>(); private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
Create a new empty ConstructorArgumentValues object.
/** * Create a new empty ConstructorArgumentValues object. */
public ConstructorArgumentValues() { }
Deep copy constructor.
Params:
  • original – the ConstructorArgumentValues to copy
/** * Deep copy constructor. * @param original the ConstructorArgumentValues to copy */
public ConstructorArgumentValues(ConstructorArgumentValues original) { addArgumentValues(original); }
Copy all given argument values into this object, using separate holder instances to keep the values independent from the original object.

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.

/** * 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(@Nullable ConstructorArgumentValues other) { if (other != null) { other.indexedArgumentValues.forEach( (index, argValue) -> addOrMergeIndexedArgumentValue(index, argValue.copy()) ); other.genericArgumentValues.stream() .filter(valueHolder -> !this.genericArgumentValues.contains(valueHolder)) .forEach(valueHolder -> addOrMergeGenericArgumentValue(valueHolder.copy())); } }
Add an argument value for the given index in the constructor argument list.
Params:
  • index – the index in the constructor argument list
  • value – the argument value
/** * Add an argument value for the given index in the constructor argument list. * @param index the index in the constructor argument list * @param value the argument value */
public void addIndexedArgumentValue(int index, @Nullable Object value) { addIndexedArgumentValue(index, new ValueHolder(value)); }
Add an argument value for the given index in the constructor argument list.
Params:
  • index – the index in the constructor argument list
  • value – the argument value
  • type – the type of the constructor argument
/** * Add an argument value for the given index in the constructor argument list. * @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, @Nullable Object value, String type) { addIndexedArgumentValue(index, new ValueHolder(value, type)); }
Add an argument value for the given index in the constructor argument list.
Params:
  • index – the index in the constructor argument list
  • newValue – the argument value in the form of a ValueHolder
/** * Add an argument value for the given index in the constructor argument list. * @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"); addOrMergeIndexedArgumentValue(index, newValue); }
Add an argument value for the given index in the constructor argument list, merging the new value (typically a collection) with the current value if demanded: see Mergeable.
Params:
  • key – the index in the constructor argument list
  • newValue – the argument value in the form of a ValueHolder
/** * Add an argument value for the given index in the constructor argument list, * 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 */
private void addOrMergeIndexedArgumentValue(Integer key, ValueHolder newValue) { ValueHolder currentValue = this.indexedArgumentValues.get(key); 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); }
Check whether an argument value has been registered for the given index.
Params:
  • index – the index in the constructor argument list
/** * 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); }
Get argument value for the given index in the constructor argument list.
Params:
  • index – the index in the constructor argument list
  • requiredType – the type to match (can be null to match untyped values only)
Returns:the ValueHolder for the argument, or null if none set
/** * 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} to match * untyped values only) * @return the ValueHolder for the argument, or {@code null} if none set */
@Nullable public ValueHolder getIndexedArgumentValue(int index, @Nullable Class<?> requiredType) { return getIndexedArgumentValue(index, requiredType, null); }
Get argument value for the given index in the constructor argument list.
Params:
  • index – the index in the constructor argument list
  • requiredType – the type to match (can be null to match untyped values only)
  • requiredName – the type to match (can be null to match unnamed values only, or empty String to match any name)
Returns:the ValueHolder for the argument, or null if none set
/** * 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} to match * untyped values only) * @param requiredName the type to match (can be {@code null} to match * unnamed values only, or empty String to match any name) * @return the ValueHolder for the argument, or {@code null} if none set */
@Nullable public ValueHolder getIndexedArgumentValue(int index, @Nullable Class<?> requiredType, @Nullable String requiredName) { Assert.isTrue(index >= 0, "Index must not be negative"); ValueHolder valueHolder = this.indexedArgumentValues.get(index); if (valueHolder != null && (valueHolder.getType() == null || (requiredType != null && ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) && (valueHolder.getName() == null || (requiredName != null && (requiredName.isEmpty() || requiredName.equals(valueHolder.getName()))))) { return valueHolder; } return null; }
Return the map of indexed argument values.
See Also:
Returns:unmodifiable Map with Integer index as key and ValueHolder as value
/** * Return the map of indexed argument values. * @return unmodifiable Map with Integer index as key and ValueHolder as value * @see ValueHolder */
public Map<Integer, ValueHolder> getIndexedArgumentValues() { return Collections.unmodifiableMap(this.indexedArgumentValues); }
Add a generic argument value to be matched by type.

Note: A single generic argument value will just be used once, rather than matched multiple times.

Params:
  • value – the argument value
/** * Add a generic argument value to be matched by type. * <p>Note: A single generic argument value will just be used once, * rather than matched multiple times. * @param value the argument value */
public void addGenericArgumentValue(Object value) { this.genericArgumentValues.add(new ValueHolder(value)); }
Add a generic argument value to be matched by type.

Note: A single generic argument value will just be used once, rather than matched multiple times.

Params:
  • value – the argument value
  • type – the type of the constructor argument
/** * Add a generic argument value to be matched by type. * <p>Note: A single generic argument value will just be used once, * rather than matched multiple times. * @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)); }
Add a generic argument value to be matched by type or name (if available).

Note: A single generic argument value will just be used once, rather than matched multiple times.

Params:
  • newValue – the argument value in the form of a ValueHolder

    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.

/** * Add a generic argument value to be matched by type or name (if available). * <p>Note: A single generic argument value will just be used once, * rather than matched multiple times. * @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)) { addOrMergeGenericArgumentValue(newValue); } }
Add a generic argument value, merging the new value (typically a collection) with the current value if demanded: see Mergeable.
Params:
  • newValue – the argument value in the form of a ValueHolder
/** * 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); }
Look for a generic argument value that matches the given type.
Params:
  • requiredType – the type to match
Returns:the ValueHolder for the argument, or null if none set
/** * Look for a generic argument value that matches the given type. * @param requiredType the type to match * @return the ValueHolder for the argument, or {@code null} if none set */
@Nullable public ValueHolder getGenericArgumentValue(Class<?> requiredType) { return getGenericArgumentValue(requiredType, null, null); }
Look for a generic argument value that matches the given type.
Params:
  • requiredType – the type to match
  • requiredName – the name to match
Returns:the ValueHolder for the argument, or null if none set
/** * 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} if none set */
@Nullable public ValueHolder getGenericArgumentValue(Class<?> requiredType, String requiredName) { return getGenericArgumentValue(requiredType, requiredName, null); }
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.
Params:
  • requiredType – the type to match (can be null to find an arbitrary next generic argument value)
  • requiredName – the name to match (can be null to not match argument values by name, or empty String to match any name)
  • usedValueHolders – a Set of ValueHolder objects that have already been used in the current resolution process and should therefore not be returned again
Returns:the ValueHolder for the argument, or null if none found
/** * 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} to find * an arbitrary next generic argument value) * @param requiredName the name to match (can be {@code null} to not * match argument values by name, or empty String to match any name) * @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} if none found */
@Nullable public ValueHolder getGenericArgumentValue(@Nullable Class<?> requiredType, @Nullable String requiredName, @Nullable Set<ValueHolder> usedValueHolders) { for (ValueHolder valueHolder : this.genericArgumentValues) { if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) { continue; } if (valueHolder.getName() != null && (requiredName == null || (!requiredName.isEmpty() && !requiredName.equals(valueHolder.getName())))) { continue; } if (valueHolder.getType() != null && (requiredType == null || !ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) { continue; } if (requiredType != null && valueHolder.getType() == null && valueHolder.getName() == null && !ClassUtils.isAssignableValue(requiredType, valueHolder.getValue())) { continue; } return valueHolder; } return null; }
Return the list of generic argument values.
See Also:
Returns:unmodifiable List of ValueHolders
/** * Return the list of generic argument values. * @return unmodifiable List of ValueHolders * @see ValueHolder */
public List<ValueHolder> getGenericArgumentValues() { 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.
Params:
  • index – the index in the constructor argument list
  • requiredType – the parameter type to match
Returns:the ValueHolder for the argument, or null if none set
/** * 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 parameter type to match * @return the ValueHolder for the argument, or {@code null} if none set */
@Nullable public ValueHolder getArgumentValue(int index, Class<?> requiredType) { 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.
Params:
  • index – the index in the constructor argument list
  • requiredType – the parameter type to match
  • requiredName – the parameter name to match
Returns:the ValueHolder for the argument, or null if none set
/** * 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 parameter type to match * @param requiredName the parameter name to match * @return the ValueHolder for the argument, or {@code null} if none set */
@Nullable public ValueHolder getArgumentValue(int index, Class<?> requiredType, String requiredName) { return getArgumentValue(index, requiredType, requiredName, null); }
Look for an argument value that either corresponds to the given index in the constructor argument list or generically matches by type.
Params:
  • index – the index in the constructor argument list
  • requiredType – the parameter type to match (can be null to find an untyped argument value)
  • requiredName – the parameter name to match (can be null to find an unnamed argument value, or empty String to match any name)
  • 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)
Returns:the ValueHolder for the argument, or null if none set
/** * 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 parameter type to match (can be {@code null} * to find an untyped argument value) * @param requiredName the parameter name to match (can be {@code null} * to find an unnamed argument value, or empty String to match any name) * @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} if none set */
@Nullable public ValueHolder getArgumentValue(int index, @Nullable Class<?> requiredType, @Nullable String requiredName, @Nullable Set<ValueHolder> usedValueHolders) { Assert.isTrue(index >= 0, "Index must not be negative"); ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName); if (valueHolder == null) { valueHolder = getGenericArgumentValue(requiredType, requiredName, usedValueHolders); } return valueHolder; }
Return the number of argument values held in this instance, counting both indexed and generic argument values.
/** * 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.
/** * 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.
/** * Clear this holder, removing all argument values. */
public void clear() { this.indexedArgumentValues.clear(); this.genericArgumentValues.clear(); } @Override public boolean equals(@Nullable 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; } Iterator<ValueHolder> it1 = this.genericArgumentValues.iterator(); Iterator<ValueHolder> it2 = that.genericArgumentValues.iterator(); while (it1.hasNext() && it2.hasNext()) { ValueHolder vh1 = it1.next(); ValueHolder vh2 = it2.next(); if (!vh1.contentEquals(vh2)) { return false; } } for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) { ValueHolder vh1 = entry.getValue(); ValueHolder vh2 = that.indexedArgumentValues.get(entry.getKey()); if (vh2 == null || !vh1.contentEquals(vh2)) { return false; } } return true; } @Override public int hashCode() { int hashCode = 7; for (ValueHolder valueHolder : this.genericArgumentValues) { hashCode = 31 * hashCode + valueHolder.contentHashCode(); } hashCode = 29 * hashCode; for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) { hashCode = 31 * hashCode + (entry.getValue().contentHashCode() ^ entry.getKey().hashCode()); } return hashCode; }
Holder for a constructor argument value, with an optional type attribute indicating the target type of the actual constructor argument.
/** * 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 { @Nullable private Object value; @Nullable private String type; @Nullable private String name; @Nullable private Object source; private boolean converted = false; @Nullable private Object convertedValue;
Create a new ValueHolder for the given value.
Params:
  • value – the argument value
/** * Create a new ValueHolder for the given value. * @param value the argument value */
public ValueHolder(@Nullable Object value) { this.value = value; }
Create a new ValueHolder for the given value and type.
Params:
  • value – the argument value
  • type – the type of the constructor argument
/** * 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(@Nullable Object value, @Nullable String type) { this.value = value; this.type = type; }
Create a new ValueHolder for the given value, type and name.
Params:
  • value – the argument value
  • type – the type of the constructor argument
  • name – the name of the constructor argument
/** * 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(@Nullable Object value, @Nullable String type, @Nullable String name) { this.value = value; this.type = type; this.name = name; }
Set the value for the constructor argument.
/** * Set the value for the constructor argument. */
public void setValue(@Nullable Object value) { this.value = value; }
Return the value for the constructor argument.
/** * Return the value for the constructor argument. */
@Nullable public Object getValue() { return this.value; }
Set the type of the constructor argument.
/** * Set the type of the constructor argument. */
public void setType(@Nullable String type) { this.type = type; }
Return the type of the constructor argument.
/** * Return the type of the constructor argument. */
@Nullable public String getType() { return this.type; }
Set the name of the constructor argument.
/** * Set the name of the constructor argument. */
public void setName(@Nullable String name) { this.name = name; }
Return the name of the constructor argument.
/** * Return the name of the constructor argument. */
@Nullable public String getName() { return this.name; }
Set the configuration source Object for this metadata element.

The exact type of the object will depend on the configuration mechanism used.

/** * Set the configuration source {@code Object} for this metadata element. * <p>The exact type of the object will depend on the configuration mechanism used. */
public void setSource(@Nullable Object source) { this.source = source; } @Override @Nullable public Object getSource() { return this.source; }
Return whether this holder contains a converted value already (true), or whether the value still needs to be converted (false).
/** * Return whether this holder contains a converted value already ({@code true}), * or whether the value still needs to be converted ({@code false}). */
public synchronized boolean isConverted() { return this.converted; }
Set the converted value of the constructor argument, after processed type conversion.
/** * Set the converted value of the constructor argument, * after processed type conversion. */
public synchronized void setConvertedValue(@Nullable Object value) { this.converted = (value != null); this.convertedValue = value; }
Return the converted value of the constructor argument, after processed type conversion.
/** * Return the converted value of the constructor argument, * after processed type conversion. */
@Nullable public synchronized Object getConvertedValue() { return this.convertedValue; }
Determine whether the content of this ValueHolder is equal to the content of the given other ValueHolder.

Note that ValueHolder does not implement equals directly, to allow for multiple ValueHolder instances with the same content to reside in the same Set.

/** * 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} * 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.

Note that ValueHolder does not implement hashCode directly, to allow for multiple ValueHolder instances with the same content to reside in the same Set.

/** * Determine whether the hash code of the content of this ValueHolder. * <p>Note that ValueHolder does not implement {@code hashCode} * 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.
/** * Create a copy of this ValueHolder: that is, an independent * ValueHolder instance with the same contents. */
public ValueHolder copy() { ValueHolder copy = new ValueHolder(this.value, this.type, this.name); copy.setSource(this.source); return copy; } } }