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}