001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util;
018
019import java.lang.reflect.GenericArrayType;
020import java.lang.reflect.ParameterizedType;
021import java.lang.reflect.Type;
022import java.lang.reflect.WildcardType;
023
024import org.springframework.lang.Nullable;
025
026/**
027 * Utility to work with Java 5 generic type parameters.
028 * Mainly for internal use within the framework.
029 *
030 * @author Ramnivas Laddad
031 * @author Juergen Hoeller
032 * @author Chris Beams
033 * @since 2.0.7
034 */
035public abstract class TypeUtils {
036
037        /**
038         * Check if the right-hand side type may be assigned to the left-hand side
039         * type following the Java generics rules.
040         * @param lhsType the target type
041         * @param rhsType the value type that should be assigned to the target type
042         * @return true if rhs is assignable to lhs
043         */
044        public static boolean isAssignable(Type lhsType, Type rhsType) {
045                Assert.notNull(lhsType, "Left-hand side type must not be null");
046                Assert.notNull(rhsType, "Right-hand side type must not be null");
047
048                // all types are assignable to themselves and to class Object
049                if (lhsType.equals(rhsType) || Object.class == lhsType) {
050                        return true;
051                }
052
053                if (lhsType instanceof Class) {
054                        Class<?> lhsClass = (Class<?>) lhsType;
055
056                        // just comparing two classes
057                        if (rhsType instanceof Class) {
058                                return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType);
059                        }
060
061                        if (rhsType instanceof ParameterizedType) {
062                                Type rhsRaw = ((ParameterizedType) rhsType).getRawType();
063
064                                // a parameterized type is always assignable to its raw class type
065                                if (rhsRaw instanceof Class) {
066                                        return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw);
067                                }
068                        }
069                        else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) {
070                                Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType();
071
072                                return isAssignable(lhsClass.getComponentType(), rhsComponent);
073                        }
074                }
075
076                // parameterized types are only assignable to other parameterized types and class types
077                if (lhsType instanceof ParameterizedType) {
078                        if (rhsType instanceof Class) {
079                                Type lhsRaw = ((ParameterizedType) lhsType).getRawType();
080
081                                if (lhsRaw instanceof Class) {
082                                        return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType);
083                                }
084                        }
085                        else if (rhsType instanceof ParameterizedType) {
086                                return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType);
087                        }
088                }
089
090                if (lhsType instanceof GenericArrayType) {
091                        Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType();
092
093                        if (rhsType instanceof Class) {
094                                Class<?> rhsClass = (Class<?>) rhsType;
095
096                                if (rhsClass.isArray()) {
097                                        return isAssignable(lhsComponent, rhsClass.getComponentType());
098                                }
099                        }
100                        else if (rhsType instanceof GenericArrayType) {
101                                Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType();
102
103                                return isAssignable(lhsComponent, rhsComponent);
104                        }
105                }
106
107                if (lhsType instanceof WildcardType) {
108                        return isAssignable((WildcardType) lhsType, rhsType);
109                }
110
111                return false;
112        }
113
114        private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) {
115                if (lhsType.equals(rhsType)) {
116                        return true;
117                }
118
119                Type[] lhsTypeArguments = lhsType.getActualTypeArguments();
120                Type[] rhsTypeArguments = rhsType.getActualTypeArguments();
121
122                if (lhsTypeArguments.length != rhsTypeArguments.length) {
123                        return false;
124                }
125
126                for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) {
127                        Type lhsArg = lhsTypeArguments[i];
128                        Type rhsArg = rhsTypeArguments[i];
129
130                        if (!lhsArg.equals(rhsArg) &&
131                                        !(lhsArg instanceof WildcardType && isAssignable((WildcardType) lhsArg, rhsArg))) {
132                                return false;
133                        }
134                }
135
136                return true;
137        }
138
139        private static boolean isAssignable(WildcardType lhsType, Type rhsType) {
140                Type[] lUpperBounds = lhsType.getUpperBounds();
141
142                // supply the implicit upper bound if none are specified
143                if (lUpperBounds.length == 0) {
144                        lUpperBounds = new Type[] { Object.class };
145                }
146
147                Type[] lLowerBounds = lhsType.getLowerBounds();
148
149                // supply the implicit lower bound if none are specified
150                if (lLowerBounds.length == 0) {
151                        lLowerBounds = new Type[] { null };
152                }
153
154                if (rhsType instanceof WildcardType) {
155                        // both the upper and lower bounds of the right-hand side must be
156                        // completely enclosed in the upper and lower bounds of the left-
157                        // hand side.
158                        WildcardType rhsWcType = (WildcardType) rhsType;
159                        Type[] rUpperBounds = rhsWcType.getUpperBounds();
160
161                        if (rUpperBounds.length == 0) {
162                                rUpperBounds = new Type[] { Object.class };
163                        }
164
165                        Type[] rLowerBounds = rhsWcType.getLowerBounds();
166
167                        if (rLowerBounds.length == 0) {
168                                rLowerBounds = new Type[] { null };
169                        }
170
171                        for (Type lBound : lUpperBounds) {
172                                for (Type rBound : rUpperBounds) {
173                                        if (!isAssignableBound(lBound, rBound)) {
174                                                return false;
175                                        }
176                                }
177
178                                for (Type rBound : rLowerBounds) {
179                                        if (!isAssignableBound(lBound, rBound)) {
180                                                return false;
181                                        }
182                                }
183                        }
184
185                        for (Type lBound : lLowerBounds) {
186                                for (Type rBound : rUpperBounds) {
187                                        if (!isAssignableBound(rBound, lBound)) {
188                                                return false;
189                                        }
190                                }
191
192                                for (Type rBound : rLowerBounds) {
193                                        if (!isAssignableBound(rBound, lBound)) {
194                                                return false;
195                                        }
196                                }
197                        }
198                }
199                else {
200                        for (Type lBound : lUpperBounds) {
201                                if (!isAssignableBound(lBound, rhsType)) {
202                                        return false;
203                                }
204                        }
205
206                        for (Type lBound : lLowerBounds) {
207                                if (!isAssignableBound(rhsType, lBound)) {
208                                        return false;
209                                }
210                        }
211                }
212
213                return true;
214        }
215
216        public static boolean isAssignableBound(@Nullable Type lhsType, @Nullable Type rhsType) {
217                if (rhsType == null) {
218                        return true;
219                }
220                if (lhsType == null) {
221                        return false;
222                }
223                return isAssignable(lhsType, rhsType);
224        }
225
226}