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}