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