@SuppressWarnings("unchecked")
  private Map<?, ?> convertToTypedMap(
      Map<?, ?> original,
      String propertyName,
      Class<?> requiredType,
      TypeDescriptor typeDescriptor) {

    if (!Map.class.isAssignableFrom(requiredType)) {
      return original;
    }

    boolean approximable = CollectionFactory.isApproximableMapType(requiredType);
    if (!approximable && !canCreateCopy(requiredType)) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Custom Map type ["
                + original.getClass().getName()
                + "] does not allow for creating a copy - injecting original Map as-is");
      }
      return original;
    }

    boolean originalAllowed = requiredType.isInstance(original);
    TypeDescriptor keyType = typeDescriptor.getMapKeyTypeDescriptor();
    TypeDescriptor valueType = typeDescriptor.getMapValueTypeDescriptor();
    if (keyType == null
        && valueType == null
        && originalAllowed
        && !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
      return original;
    }

    Iterator<?> it;
    try {
      it = original.entrySet().iterator();
      if (it == null) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Map of type ["
                  + original.getClass().getName()
                  + "] returned null Iterator - injecting original Map as-is");
        }
        return original;
      }
    } catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Cannot access Map of type ["
                + original.getClass().getName()
                + "] - injecting original Map as-is: "
                + ex);
      }
      return original;
    }

    Map<Object, Object> convertedCopy;
    try {
      if (approximable) {
        convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
      } else {
        convertedCopy =
            (Map<Object, Object>) ReflectionUtils.accessibleConstructor(requiredType).newInstance();
      }
    } catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Cannot create copy of Map type ["
                + original.getClass().getName()
                + "] - injecting original Map as-is: "
                + ex);
      }
      return original;
    }

    while (it.hasNext()) {
      Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next();
      Object key = entry.getKey();
      Object value = entry.getValue();
      String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
      Object convertedKey =
          convertIfNecessary(
              keyedPropertyName, null, key, (keyType != null ? keyType.getType() : null), keyType);
      Object convertedValue =
          convertIfNecessary(
              keyedPropertyName,
              null,
              value,
              (valueType != null ? valueType.getType() : null),
              valueType);
      try {
        convertedCopy.put(convertedKey, convertedValue);
      } catch (Throwable ex) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Map type ["
                  + original.getClass().getName()
                  + "] seems to be read-only - injecting original Map as-is: "
                  + ex);
        }
        return original;
      }
      originalAllowed = originalAllowed && (key == convertedKey) && (value == convertedValue);
    }
    return (originalAllowed ? original : convertedCopy);
  }
  @SuppressWarnings("unchecked")
  private Collection<?> convertToTypedCollection(
      Collection<?> original,
      String propertyName,
      Class<?> requiredType,
      TypeDescriptor typeDescriptor) {

    if (!Collection.class.isAssignableFrom(requiredType)) {
      return original;
    }

    boolean approximable = CollectionFactory.isApproximableCollectionType(requiredType);
    if (!approximable && !canCreateCopy(requiredType)) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Custom Collection type ["
                + original.getClass().getName()
                + "] does not allow for creating a copy - injecting original Collection as-is");
      }
      return original;
    }

    boolean originalAllowed = requiredType.isInstance(original);
    TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
    if (elementType == null
        && originalAllowed
        && !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
      return original;
    }

    Iterator<?> it;
    try {
      it = original.iterator();
      if (it == null) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Collection of type ["
                  + original.getClass().getName()
                  + "] returned null Iterator - injecting original Collection as-is");
        }
        return original;
      }
    } catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Cannot access Collection of type ["
                + original.getClass().getName()
                + "] - injecting original Collection as-is: "
                + ex);
      }
      return original;
    }

    Collection<Object> convertedCopy;
    try {
      if (approximable) {
        convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
      } else {
        convertedCopy =
            (Collection<Object>) ReflectionUtils.accessibleConstructor(requiredType).newInstance();
      }
    } catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Cannot create copy of Collection type ["
                + original.getClass().getName()
                + "] - injecting original Collection as-is: "
                + ex);
      }
      return original;
    }

    int i = 0;
    for (; it.hasNext(); i++) {
      Object element = it.next();
      String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
      Object convertedElement =
          convertIfNecessary(
              indexedPropertyName,
              null,
              element,
              (elementType != null ? elementType.getType() : null),
              elementType);
      try {
        convertedCopy.add(convertedElement);
      } catch (Throwable ex) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Collection type ["
                  + original.getClass().getName()
                  + "] seems to be read-only - injecting original Collection as-is: "
                  + ex);
        }
        return original;
      }
      originalAllowed = originalAllowed && (element == convertedElement);
    }
    return (originalAllowed ? original : convertedCopy);
  }