/**
   * Create an array of arguments to invoke a constructor or factory method, given the resolved
   * constructor argument values.
   */
  private ArgumentsHolder createArgumentArray(
      String beanName,
      RootBeanDefinition mbd,
      ConstructorArgumentValues resolvedValues,
      BeanWrapper bw,
      Class<?>[] paramTypes,
      String[] paramNames,
      Object methodOrCtor,
      boolean autowiring)
      throws UnsatisfiedDependencyException {

    String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method");
    TypeConverter converter =
        (this.beanFactory.getCustomTypeConverter() != null
            ? this.beanFactory.getCustomTypeConverter()
            : bw);

    ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
    Set<ConstructorArgumentValues.ValueHolder> usedValueHolders =
        new HashSet<ConstructorArgumentValues.ValueHolder>(paramTypes.length);
    Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);

    for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
      Class<?> paramType = paramTypes[paramIndex];
      String paramName = (paramNames != null ? paramNames[paramIndex] : null);
      // Try to find matching constructor argument value, either indexed or generic.
      ConstructorArgumentValues.ValueHolder valueHolder =
          resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
      // If we couldn't find a direct match and are not supposed to autowire,
      // let's try the next generic, untyped argument value as fallback:
      // it could match after type conversion (for example, String -> int).
      if (valueHolder == null && !autowiring) {
        valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
      }
      if (valueHolder != null) {
        // We found a potential match - let's give it a try.
        // Do not consider the same value definition multiple times!
        usedValueHolders.add(valueHolder);
        Object originalValue = valueHolder.getValue();
        Object convertedValue;
        if (valueHolder.isConverted()) {
          convertedValue = valueHolder.getConvertedValue();
          args.preparedArguments[paramIndex] = convertedValue;
        } else {
          ConstructorArgumentValues.ValueHolder sourceHolder =
              (ConstructorArgumentValues.ValueHolder) valueHolder.getSource();
          Object sourceValue = sourceHolder.getValue();
          try {
            convertedValue =
                converter.convertIfNecessary(
                    originalValue,
                    paramType,
                    MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex));
            // TODO re-enable once race condition has been found (SPR-7423)
            /*
            if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) {
            	// Either a converted value or still the original one: store converted value.
            	sourceHolder.setConvertedValue(convertedValue);
            	args.preparedArguments[paramIndex] = convertedValue;
            }
            else {
            */
            args.resolveNecessary = true;
            args.preparedArguments[paramIndex] = sourceValue;
            // }
          } catch (TypeMismatchException ex) {
            throw new UnsatisfiedDependencyException(
                mbd.getResourceDescription(),
                beanName,
                paramIndex,
                paramType,
                "Could not convert "
                    + methodType
                    + " argument value of type ["
                    + ObjectUtils.nullSafeClassName(valueHolder.getValue())
                    + "] to required type ["
                    + paramType.getName()
                    + "]: "
                    + ex.getMessage());
          }
        }
        args.arguments[paramIndex] = convertedValue;
        args.rawArguments[paramIndex] = originalValue;
      } else {
        // No explicit match found: we're either supposed to autowire or
        // have to fail creating an argument array for the given constructor.
        if (!autowiring) {
          throw new UnsatisfiedDependencyException(
              mbd.getResourceDescription(),
              beanName,
              paramIndex,
              paramType,
              "Ambiguous "
                  + methodType
                  + " argument types - "
                  + "did you specify the correct bean references as "
                  + methodType
                  + " arguments?");
        }
        try {
          MethodParameter param = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex);
          Object autowiredArgument =
              resolveAutowiredArgument(param, beanName, autowiredBeanNames, converter);
          args.rawArguments[paramIndex] = autowiredArgument;
          args.arguments[paramIndex] = autowiredArgument;
          args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
          args.resolveNecessary = true;
        } catch (BeansException ex) {
          throw new UnsatisfiedDependencyException(
              mbd.getResourceDescription(), beanName, paramIndex, paramType, ex);
        }
      }
    }

    for (String autowiredBeanName : autowiredBeanNames) {
      this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
      if (this.beanFactory.logger.isDebugEnabled()) {
        this.beanFactory.logger.debug(
            "Autowiring by type from bean name '"
                + beanName
                + "' via "
                + methodType
                + " to bean named '"
                + autowiredBeanName
                + "'");
      }
    }

    return args;
  }
  /**
   * Resolve the constructor arguments for this bean into the resolvedValues object. This may
   * involve looking up other beans.
   *
   * <p>This method is also used for handling invocations of static factory methods.
   */
  private int resolveConstructorArguments(
      String beanName,
      RootBeanDefinition mbd,
      BeanWrapper bw,
      ConstructorArgumentValues cargs,
      ConstructorArgumentValues resolvedValues) {

    TypeConverter converter =
        (this.beanFactory.getCustomTypeConverter() != null
            ? this.beanFactory.getCustomTypeConverter()
            : bw);
    BeanDefinitionValueResolver valueResolver =
        new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);

    int minNrOfArgs = cargs.getArgumentCount();

    for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry :
        cargs.getIndexedArgumentValues().entrySet()) {
      int index = entry.getKey();
      if (index < 0) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid constructor argument index: " + index);
      }
      if (index > minNrOfArgs) {
        minNrOfArgs = index + 1;
      }
      ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
      if (valueHolder.isConverted()) {
        resolvedValues.addIndexedArgumentValue(index, valueHolder);
      } else {
        Object resolvedValue =
            valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
        ConstructorArgumentValues.ValueHolder resolvedValueHolder =
            new ConstructorArgumentValues.ValueHolder(
                resolvedValue, valueHolder.getType(), valueHolder.getName());
        resolvedValueHolder.setSource(valueHolder);
        resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
      }
    }

    for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
      if (valueHolder.isConverted()) {
        resolvedValues.addGenericArgumentValue(valueHolder);
      } else {
        Object resolvedValue =
            valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
        ConstructorArgumentValues.ValueHolder resolvedValueHolder =
            new ConstructorArgumentValues.ValueHolder(
                resolvedValue, valueHolder.getType(), valueHolder.getName());
        resolvedValueHolder.setSource(valueHolder);
        resolvedValues.addGenericArgumentValue(resolvedValueHolder);
      }
    }

    return minNrOfArgs;
  }