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