Open Source Repository

Home /spring/spring-core-3.0.5 | Repository Home



org/springframework/util/TypeUtils.java
/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.util;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;

import org.springframework.util.ClassUtils;

/**
 * Utility to work with Java 5 generic type parameters.
 * Mainly for internal use within the framework.
 *
 @author Ramnivas Laddad
 @author Juergen Hoeller
 @author Chris Beams
 @since 2.0.7
 */
public abstract class TypeUtils {

  /**
   * Check if the right-hand side type may be assigned to the left-hand side
   * type following the Java generics rules.
   @param lhsType the target type
   @param rhsType the value type that should be assigned to the target type
   @return true if rhs is assignable to lhs
   */
  public static boolean isAssignable(Type lhsType, Type rhsType) {
    Assert.notNull(lhsType, "Left-hand side type must not be null");
    Assert.notNull(rhsType, "Right-hand side type must not be null");

    // all types are assignable to themselves and to class Object
    if (lhsType.equals(rhsType|| lhsType.equals(Object.class)) {
      return true;
    }

    if (lhsType instanceof Class<?>) {
      Class<?> lhsClass = (Class<?>lhsType;

      // just comparing two classes
      if (rhsType instanceof Class<?>) {
        return ClassUtils.isAssignable(lhsClass, (Class<?>rhsType);
      }

      if (rhsType instanceof ParameterizedType) {
        Type rhsRaw = ((ParameterizedTyperhsType).getRawType();

        // a parameterized type is always assignable to its raw class type
        if (rhsRaw instanceof Class<?>) {
          return ClassUtils.isAssignable(lhsClass, (Class<?>rhsRaw);
        }
      }
      else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) {
        Type rhsComponent = ((GenericArrayTyperhsType).getGenericComponentType();

        return isAssignable(lhsClass.getComponentType(), rhsComponent);
      }
    }

    // parameterized types are only assignable to other parameterized types and class types
    if (lhsType instanceof ParameterizedType) {
      if (rhsType instanceof Class<?>) {
        Type lhsRaw = ((ParameterizedTypelhsType).getRawType();

        if (lhsRaw instanceof Class<?>) {
          return ClassUtils.isAssignable((Class<?>lhsRaw, (Class<?>rhsType);
        }
      }
      else if (rhsType instanceof ParameterizedType) {
        return isAssignable((ParameterizedTypelhsType, (ParameterizedTyperhsType);
      }
    }

    if (lhsType instanceof GenericArrayType) {
      Type lhsComponent = ((GenericArrayTypelhsType).getGenericComponentType();

      if (rhsType instanceof Class<?>) {
        Class<?> rhsClass = (Class<?>rhsType;

        if (rhsClass.isArray()) {
          return isAssignable(lhsComponent, rhsClass.getComponentType());
        }
      }
      else if (rhsType instanceof GenericArrayType) {
        Type rhsComponent = ((GenericArrayTyperhsType).getGenericComponentType();

        return isAssignable(lhsComponent, rhsComponent);
      }
    }

    if (lhsType instanceof WildcardType) {
      return isAssignable((WildcardTypelhsType, rhsType);
    }

    return false;
  }

  private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) {
    if (lhsType.equals(rhsType)) {
      return true;
    }

    Type[] lhsTypeArguments = lhsType.getActualTypeArguments();
    Type[] rhsTypeArguments = rhsType.getActualTypeArguments();

    if (lhsTypeArguments.length != rhsTypeArguments.length) {
      return false;
    }

    for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) {
      Type lhsArg = lhsTypeArguments[i];
      Type rhsArg = rhsTypeArguments[i];

      if (!lhsArg.equals(rhsArg&&
          !(lhsArg instanceof WildcardType && isAssignable((WildcardTypelhsArg, rhsArg))) {
        return false;
      }
    }

    return true;
  }

  private static boolean isAssignable(WildcardType lhsType, Type rhsType) {
    Type[] lUpperBounds = lhsType.getUpperBounds();

    // supply the implicit upper bound if none are specified
    if (lUpperBounds.length == 0) {
      lUpperBounds = new Type[] { Object.class };
    }

    Type[] lLowerBounds = lhsType.getLowerBounds();

    // supply the implicit lower bound if none are specified
    if (lLowerBounds.length == 0) {
      lLowerBounds = new Type[] { null };
    }

    if (rhsType instanceof WildcardType) {
      // both the upper and lower bounds of the right-hand side must be
      // completely enclosed in the upper and lower bounds of the left-
      // hand side.
      WildcardType rhsWcType = (WildcardTyperhsType;
      Type[] rUpperBounds = rhsWcType.getUpperBounds();

      if (rUpperBounds.length == 0) {
        rUpperBounds = new Type[] { Object.class };
      }

      Type[] rLowerBounds = rhsWcType.getLowerBounds();

      if (rLowerBounds.length == 0) {
        rLowerBounds = new Type[] { null };
      }

      for (Type lBound : lUpperBounds) {
        for (Type rBound : rUpperBounds) {
          if (!isAssignableBound(lBound, rBound)) {
            return false;
          }
        }

        for (Type rBound : rLowerBounds) {
          if (!isAssignableBound(lBound, rBound)) {
            return false;
          }
        }
      }

      for (Type lBound : lLowerBounds) {
        for (Type rBound : rUpperBounds) {
          if (!isAssignableBound(rBound, lBound)) {
            return false;
          }
        }

        for (Type rBound : rLowerBounds) {
          if (!isAssignableBound(rBound, lBound)) {
            return false;
          }
        }
      }
    }
    else {
      for (Type lBound : lUpperBounds) {
        if (!isAssignableBound(lBound, rhsType)) {
          return false;
        }
      }

      for (Type lBound : lLowerBounds) {
        if (!isAssignableBound(rhsType, lBound)) {
          return false;
        }
      }
    }

    return true;
  }
  
  public static boolean isAssignableBound(Type lhsType, Type rhsType) {
    if (rhsType == null) {
      return true;
    }

    if (lhsType == null) {
      return false;
    }
    return isAssignable(lhsType, rhsType);
  }

}