View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.util;
    18
    19  import java.lang.reflect.GenericArrayType;
    20  import java.lang.reflect.ParameterizedType;
    21  import java.lang.reflect.Type;
    22  import java.lang.reflect.WildcardType;
    23
    24  /**
    25   * Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the
    26   * <a href="http://projects.spring.io/spring-framework/">Spring Framework</a>, specifically the
    27   * <a href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/TypeUtils.html">TypeUtils</a>
    28   * class.
    29   *
    30   * @see java.lang.reflect.Type
    31   * @see java.lang.reflect.GenericArrayType
    32   * @see java.lang.reflect.ParameterizedType
    33   * @see java.lang.reflect.WildcardType
    34   * @see java.lang.Class
    35   * @since 2.1
    36   */
    37  public final class TypeUtil {
    38      private TypeUtil() {
    39      }
    40
    41      /**
    42       * Indicates if two {@link Type}s are assignment compatible.
    43       *
    44       * @param lhs the left hand side to check assignability to
    45       * @param rhs the right hand side to check assignability from
    46       * @return {@code true} if it is legal to assign a variable of type {@code rhs} to a variable of type {@code lhs}
    47       * @see Class#isAssignableFrom(Class)
    48       */
    49      public static boolean isAssignable(final Type lhs, final Type rhs) {
    50          Assert.requireNonNull(lhs, "No left hand side type provided");
    51          Assert.requireNonNull(rhs, "No right hand side type provided");
    52          if (lhs.equals(rhs)) {
    53              return true;
    54          }
    55          if (Object.class.equals(lhs)) {
    56              // everything is assignable to Object
    57              return true;
    58          }
    59          // raw type on left
    60          if (lhs instanceof Class<?>) {
    61              final Class<?> lhsClass = (Class<?>) lhs;
    62              if (rhs instanceof Class<?>) {
    63                  // no generics involved
    64                  final Class<?> rhsClass = (Class<?>) rhs;
    65                  return lhsClass.isAssignableFrom(rhsClass);
    66              }
    67              if (rhs instanceof ParameterizedType) {
    68                  // check to see if the parameterized type has the same raw type as the lhs; this is legal
    69                  final Type rhsRawType = ((ParameterizedType) rhs).getRawType();
    70                  if (rhsRawType instanceof Class<?>) {
    71                      return lhsClass.isAssignableFrom((Class<?>) rhsRawType);
    72                  }
    73              }
    74              if (lhsClass.isArray() && rhs instanceof GenericArrayType) {
    75                  // check for compatible array component types
    76                  return isAssignable(lhsClass.getComponentType(), ((GenericArrayType) rhs).getGenericComponentType());
    77              }
    78          }
    79          // parameterized type on left
    80          if (lhs instanceof ParameterizedType) {
    81              final ParameterizedType lhsType = (ParameterizedType) lhs;
    82              if (rhs instanceof Class<?>) {
    83                  final Type lhsRawType = lhsType.getRawType();
    84                  if (lhsRawType instanceof Class<?>) {
    85                      return ((Class<?>) lhsRawType).isAssignableFrom((Class<?>) rhs);
    86                  }
    87              } else if (rhs instanceof ParameterizedType) {
    88                  final ParameterizedType rhsType = (ParameterizedType) rhs;
    89                  return isParameterizedAssignable(lhsType, rhsType);
    90              }
    91          }
    92          // generic array type on left
    93          if (lhs instanceof GenericArrayType) {
    94              final Type lhsComponentType = ((GenericArrayType) lhs).getGenericComponentType();
    95              if (rhs instanceof Class<?>) {
    96                  // raw type on right
    97                  final Class<?> rhsClass = (Class<?>) rhs;
    98                  if (rhsClass.isArray()) {
    99                      return isAssignable(lhsComponentType, rhsClass.getComponentType());
    100                 }
    101             } else if (rhs instanceof GenericArrayType) {
    102                 return isAssignable(lhsComponentType, ((GenericArrayType) rhs).getGenericComponentType());
    103             }
    104         }
    105         // wildcard type on left
    106         if (lhs instanceof WildcardType) {
    107             return isWildcardAssignable((WildcardType) lhs, rhs);
    108         }
    109         // strange...
    110         return false;
    111     }
    112
    113     private static boolean isParameterizedAssignable(final ParameterizedType lhs, final ParameterizedType rhs) {
    114         if (lhs.equals(rhs)) {
    115             // that was easy
    116             return true;
    117         }
    118         final Type[] lhsTypeArguments = lhs.getActualTypeArguments();
    119         final Type[] rhsTypeArguments = rhs.getActualTypeArguments();
    120         final int size = lhsTypeArguments.length;
    121         if (rhsTypeArguments.length != size) {
    122             // clearly incompatible types
    123             return false;
    124         }
    125         for (int i = 0; i < size; i++) {
    126             // verify all type arguments are assignable
    127             final Type lhsArgument = lhsTypeArguments[i];
    128             final Type rhsArgument = rhsTypeArguments[i];
    129             if (!lhsArgument.equals(rhsArgument) &&
    130                 !(lhsArgument instanceof WildcardType &&
    131                     isWildcardAssignable((WildcardType) lhsArgument, rhsArgument))) {
    132                 return false;
    133             }
    134         }
    135         return true;
    136     }
    137
    138     private static boolean isWildcardAssignable(final WildcardType lhs, final Type rhs) {
    139         final Type[] lhsUpperBounds = getEffectiveUpperBounds(lhs);
    140         final Type[] lhsLowerBounds = getEffectiveLowerBounds(lhs);
    141         if (rhs instanceof WildcardType) {
    142             // oh boy, this scenario requires checking a lot of assignability!
    143             final WildcardType rhsType = (WildcardType) rhs;
    144             final Type[] rhsUpperBounds = getEffectiveUpperBounds(rhsType);
    145             final Type[] rhsLowerBounds = getEffectiveLowerBounds(rhsType);
    146             for (final Type lhsUpperBound : lhsUpperBounds) {
    147                 for (final Type rhsUpperBound : rhsUpperBounds) {
    148                     if (!isBoundAssignable(lhsUpperBound, rhsUpperBound)) {
    149                         return false;
    150                     }
    151                 }
    152                 for (final Type rhsLowerBound : rhsLowerBounds) {
    153                     if (!isBoundAssignable(lhsUpperBound, rhsLowerBound)) {
    154                         return false;
    155                     }
    156                 }
    157             }
    158             for (final Type lhsLowerBound : lhsLowerBounds) {
    159                 for (final Type rhsUpperBound : rhsUpperBounds) {
    160                     if (!isBoundAssignable(rhsUpperBound, lhsLowerBound)) {
    161                         return false;
    162                     }
    163                 }
    164                 for (final Type rhsLowerBound : rhsLowerBounds) {
    165                     if (!isBoundAssignable(rhsLowerBound, lhsLowerBound)) {
    166                         return false;
    167                     }
    168                 }
    169             }
    170         } else {
    171             // phew, far less bounds to check
    172             for (final Type lhsUpperBound : lhsUpperBounds) {
    173                 if (!isBoundAssignable(lhsUpperBound, rhs)) {
    174                     return false;
    175                 }
    176             }
    177             for (final Type lhsLowerBound : lhsLowerBounds) {
    178                 if (!isBoundAssignable(lhsLowerBound, rhs)) {
    179                     return false;
    180                 }
    181             }
    182         }
    183         return true;
    184     }
    185
    186     private static Type[] getEffectiveUpperBounds(final WildcardType type) {
    187         final Type[] upperBounds = type.getUpperBounds();
    188         return upperBounds.length == 0 ? new Type[]{Object.class} : upperBounds;
    189     }
    190
    191     private static Type[] getEffectiveLowerBounds(final WildcardType type) {
    192         final Type[] lowerBounds = type.getLowerBounds();
    193         return lowerBounds.length == 0 ? new Type[]{null} : lowerBounds;
    194     }
    195
    196     private static boolean isBoundAssignable(final Type lhs, final Type rhs) {
    197         return (rhs == null) || ((lhs != null) && isAssignable(lhs, rhs));
    198     }
    199 }