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  import java.util.Objects;
24  
25  /**
26   * Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the
27   * <a href="http://projects.spring.io/spring-framework/">Spring Framework</a>, specifically the
28   * <a href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/TypeUtils.html">TypeUtils</a>
29   * class.
30   *
31   * @see java.lang.reflect.Type
32   * @see java.lang.reflect.GenericArrayType
33   * @see java.lang.reflect.ParameterizedType
34   * @see java.lang.reflect.WildcardType
35   * @see java.lang.Class
36   * @since 2.1
37   */
38  public final class TypeUtil {
39      private TypeUtil() {
40      }
41  
42      /**
43       * Indicates if two {@link Type}s are assignment compatible.
44       *
45       * @param lhs the left hand side to check assignability to
46       * @param rhs the right hand side to check assignability from
47       * @return {@code true} if it is legal to assign a variable of type {@code rhs} to a variable of type {@code lhs}
48       * @see Class#isAssignableFrom(Class)
49       */
50      public static boolean isAssignable(final Type lhs, final Type rhs) {
51          Objects.requireNonNull(lhs, "No left hand side type provided");
52          Objects.requireNonNull(rhs, "No right hand side type provided");
53          if (lhs.equals(rhs)) {
54              return true;
55          }
56          if (Object.class.equals(lhs)) {
57              // everything is assignable to Object
58              return true;
59          }
60          // raw type on left
61          if (lhs instanceof Class<?>) {
62              final Class<?> lhsClass = (Class<?>) lhs;
63              if (rhs instanceof Class<?>) {
64                  // no generics involved
65                  final Class<?> rhsClass = (Class<?>) rhs;
66                  return lhsClass.isAssignableFrom(rhsClass);
67              }
68              if (rhs instanceof ParameterizedType) {
69                  // check to see if the parameterized type has the same raw type as the lhs; this is legal
70                  final Type rhsRawType = ((ParameterizedType) rhs).getRawType();
71                  if (rhsRawType instanceof Class<?>) {
72                      return lhsClass.isAssignableFrom((Class<?>) rhsRawType);
73                  }
74              }
75              if (lhsClass.isArray() && rhs instanceof GenericArrayType) {
76                  // check for compatible array component types
77                  return isAssignable(lhsClass.getComponentType(), ((GenericArrayType) rhs).getGenericComponentType());
78              }
79          }
80          // parameterized type on left
81          if (lhs instanceof ParameterizedType) {
82              final ParameterizedType lhsType = (ParameterizedType) lhs;
83              if (rhs instanceof Class<?>) {
84                  final Type lhsRawType = lhsType.getRawType();
85                  if (lhsRawType instanceof Class<?>) {
86                      return ((Class<?>) lhsRawType).isAssignableFrom((Class<?>) rhs);
87                  }
88              } else if (rhs instanceof ParameterizedType) {
89                  final ParameterizedType rhsType = (ParameterizedType) rhs;
90                  return isParameterizedAssignable(lhsType, rhsType);
91              }
92          }
93          // generic array type on left
94          if (lhs instanceof GenericArrayType) {
95              final Type lhsComponentType = ((GenericArrayType) lhs).getGenericComponentType();
96              if (rhs instanceof Class<?>) {
97                  // raw type on right
98                  final Class<?> rhsClass = (Class<?>) rhs;
99                  if (rhsClass.isArray()) {
100                     return isAssignable(lhsComponentType, rhsClass.getComponentType());
101                 }
102             } else if (rhs instanceof GenericArrayType) {
103                 return isAssignable(lhsComponentType, ((GenericArrayType) rhs).getGenericComponentType());
104             }
105         }
106         // wildcard type on left
107         if (lhs instanceof WildcardType) {
108             return isWildcardAssignable((WildcardType) lhs, rhs);
109         }
110         // strange...
111         return false;
112     }
113 
114     private static boolean isParameterizedAssignable(final ParameterizedType lhs, final ParameterizedType rhs) {
115         if (lhs.equals(rhs)) {
116             // that was easy
117             return true;
118         }
119         final Type[] lhsTypeArguments = lhs.getActualTypeArguments();
120         final Type[] rhsTypeArguments = rhs.getActualTypeArguments();
121         final int size = lhsTypeArguments.length;
122         if (rhsTypeArguments.length != size) {
123             // clearly incompatible types
124             return false;
125         }
126         for (int i = 0; i < size; i++) {
127             // verify all type arguments are assignable
128             final Type lhsArgument = lhsTypeArguments[i];
129             final Type rhsArgument = rhsTypeArguments[i];
130             if (!lhsArgument.equals(rhsArgument) &&
131                 !(lhsArgument instanceof WildcardType &&
132                     isWildcardAssignable((WildcardType) lhsArgument, rhsArgument))) {
133                 return false;
134             }
135         }
136         return true;
137     }
138 
139     private static boolean isWildcardAssignable(final WildcardType lhs, final Type rhs) {
140         final Type[] lhsUpperBounds = getEffectiveUpperBounds(lhs);
141         final Type[] lhsLowerBounds = getEffectiveLowerBounds(lhs);
142         if (rhs instanceof WildcardType) {
143             // oh boy, this scenario requires checking a lot of assignability!
144             final WildcardType rhsType = (WildcardType) rhs;
145             final Type[] rhsUpperBounds = getEffectiveUpperBounds(rhsType);
146             final Type[] rhsLowerBounds = getEffectiveLowerBounds(rhsType);
147             for (final Type lhsUpperBound : lhsUpperBounds) {
148                 for (final Type rhsUpperBound : rhsUpperBounds) {
149                     if (!isBoundAssignable(lhsUpperBound, rhsUpperBound)) {
150                         return false;
151                     }
152                 }
153                 for (final Type rhsLowerBound : rhsLowerBounds) {
154                     if (!isBoundAssignable(lhsUpperBound, rhsLowerBound)) {
155                         return false;
156                     }
157                 }
158             }
159             for (final Type lhsLowerBound : lhsLowerBounds) {
160                 for (final Type rhsUpperBound : rhsUpperBounds) {
161                     if (!isBoundAssignable(rhsUpperBound, lhsLowerBound)) {
162                         return false;
163                     }
164                 }
165                 for (final Type rhsLowerBound : rhsLowerBounds) {
166                     if (!isBoundAssignable(rhsLowerBound, lhsLowerBound)) {
167                         return false;
168                     }
169                 }
170             }
171         } else {
172             // phew, far less bounds to check
173             for (final Type lhsUpperBound : lhsUpperBounds) {
174                 if (!isBoundAssignable(lhsUpperBound, rhs)) {
175                     return false;
176                 }
177             }
178             for (final Type lhsLowerBound : lhsLowerBounds) {
179                 if (!isBoundAssignable(lhsLowerBound, rhs)) {
180                     return false;
181                 }
182             }
183         }
184         return true;
185     }
186 
187     private static Type[] getEffectiveUpperBounds(final WildcardType type) {
188         final Type[] upperBounds = type.getUpperBounds();
189         return upperBounds.length == 0 ? new Type[]{Object.class} : upperBounds;
190     }
191 
192     private static Type[] getEffectiveLowerBounds(final WildcardType type) {
193         final Type[] lowerBounds = type.getLowerBounds();
194         return lowerBounds.length == 0 ? new Type[]{null} : lowerBounds;
195     }
196 
197     private static boolean isBoundAssignable(final Type lhs, final Type rhs) {
198         return (rhs == null) || ((lhs != null) && isAssignable(lhs, rhs));
199     }
200 }