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