/* * Copyright 2002-2019 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 * * 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.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
* @since 09.11.2003
* @see BeanDefinition#getConstructorArgumentValues
*/
public class ConstructorArgumentValues {
private final Map 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.
* @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.
* @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.
* @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 {@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.
* @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.
* @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.
* @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 || "".equals(requiredName) ||
(requiredName != null && requiredName.equals(valueHolder.getName())))) {
return valueHolder;
}
return null;
}
/**
* Return the map of indexed argument values.
* @return unmodifiable Map with Integer index as key and ValueHolder as value
* @see ValueHolder
*/
public Map 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.
* @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.
* @param 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.
*/
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 {@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 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 ({@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.
*/
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.
*/
@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 {@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 {@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.
*/
public ValueHolder copy() {
ValueHolder copy = new ValueHolder(this.value, this.type, this.name);
copy.setSource(this.source);
return copy;
}
}
}