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
024/**
025 * Utility to work with Java 5 generic type parameters.
026 * Mainly for internal use within the framework.
027 *
028 * @author Ramnivas Laddad
029 * @author Juergen Hoeller
030 * @author Chris Beams
031 * @since 2.0.7
032 */
033public abstract class TypeUtils {
034
035        /**
036         * Check if the right-hand side type may be assigned to the left-hand side
037         * type following the Java generics rules.
038         * @param lhsType the target type
039         * @param rhsType the value type that should be assigned to the target type
040         * @return true if rhs is assignable to lhs
041         */
042        public static boolean isAssignable(Type lhsType, Type rhsType) {
043                Assert.notNull(lhsType, "Left-hand side type must not be null");
044                Assert.notNull(rhsType, "Right-hand side type must not be null");
045
046                // all types are assignable to themselves and to class Object
047                if (lhsType.equals(rhsType) || Object.class == lhsType) {
048                        return true;
049                }
050
051                if (lhsType instanceof Class) {
052                        Class<?> lhsClass = (Class<?>) lhsType;
053
054                        // just comparing two classes
055                        if (rhsType instanceof Class) {
056                                return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType);
057                        }
058
059                        if (rhsType instanceof ParameterizedType) {
060                                Type rhsRaw = ((ParameterizedType) rhsType).getRawType();
061
062                                // a parameterized type is always assignable to its raw class type
063                                if (rhsRaw instanceof Class) {
064                                        return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw);
065                                }
066                        }
067                        else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) {
068                                Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType();
069
070                                return isAssignable(lhsClass.getComponentType(), rhsComponent);
071                        }
072                }
073
074                // parameterized types are only assignable to other parameterized types and class types
075                if (lhsType instanceof ParameterizedType) {
076                        if (rhsType instanceof Class) {
077                                Type lhsRaw = ((ParameterizedType) lhsType).getRawType();
078
079                                if (lhsRaw instanceof Class) {
080                                        return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType);
081                                }
082                        }
083                        else if (rhsType instanceof ParameterizedType) {
084                                return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType);
085                        }
086                }
087
088                if (lhsType instanceof GenericArrayType) {
089                        Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType();
090
091                        if (rhsType instanceof Class) {
092                                Class<?> rhsClass = (Class<?>) rhsType;
093
094                                if (rhsClass.isArray()) {
095                                        return isAssignable(lhsComponent, rhsClass.getComponentType());
096                                }
097                        }
098                        else if (rhsType instanceof GenericArrayType) {
099                                Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType();
100
101                                return isAssignable(lhsComponent, rhsComponent);
102                        }
103                }
104
105                if (lhsType instanceof WildcardType) {
106                        return isAssignable((WildcardType) lhsType, rhsType);
107                }
108
109                return false;
110        }
111
112        private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) {
113                if (lhsType.equals(rhsType)) {
114                        return true;
115                }
116
117                Type[] lhsTypeArguments = lhsType.getActualTypeArguments();
118                Type[] rhsTypeArguments = rhsType.getActualTypeArguments();
119
120                if (lhsTypeArguments.length != rhsTypeArguments.length) {
121                        return false;
122                }
123
124                for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) {
125                        Type lhsArg = lhsTypeArguments[i];
126                        Type rhsArg = rhsTypeArguments[i];
127
128                        if (!lhsArg.equals(rhsArg) &&
129                                        !(lhsArg instanceof WildcardType && isAssignable((WildcardType) lhsArg, rhsArg))) {
130                                return false;
131                        }
132                }
133
134                return true;
135        }
136
137        private static boolean isAssignable(WildcardType lhsType, Type rhsType) {
138                Type[] lUpperBounds = lhsType.getUpperBounds();
139
140                // supply the implicit upper bound if none are specified
141                if (lUpperBounds.length == 0) {
142                        lUpperBounds = new Type[] { Object.class };
143                }
144
145                Type[] lLowerBounds = lhsType.getLowerBounds();
146
147                // supply the implicit lower bound if none are specified
148                if (lLowerBounds.length == 0) {
149                        lLowerBounds = new Type[] { null };
150                }
151
152                if (rhsType instanceof WildcardType) {
153                        // both the upper and lower bounds of the right-hand side must be
154                        // completely enclosed in the upper and lower bounds of the left-
155                        // hand side.
156                        WildcardType rhsWcType = (WildcardType) rhsType;
157                        Type[] rUpperBounds = rhsWcType.getUpperBounds();
158
159                        if (rUpperBounds.length == 0) {
160                                rUpperBounds = new Type[] { Object.class };
161                        }
162
163                        Type[] rLowerBounds = rhsWcType.getLowerBounds();
164
165                        if (rLowerBounds.length == 0) {
166                                rLowerBounds = new Type[] { null };
167                        }
168
169                        for (Type lBound : lUpperBounds) {
170                                for (Type rBound : rUpperBounds) {
171                                        if (!isAssignableBound(lBound, rBound)) {
172                                                return false;
173                                        }
174                                }
175
176                                for (Type rBound : rLowerBounds) {
177                                        if (!isAssignableBound(lBound, rBound)) {
178                                                return false;
179                                        }
180                                }
181                        }
182
183                        for (Type lBound : lLowerBounds) {
184                                for (Type rBound : rUpperBounds) {
185                                        if (!isAssignableBound(rBound, lBound)) {
186                                                return false;
187                                        }
188                                }
189
190                                for (Type rBound : rLowerBounds) {
191                                        if (!isAssignableBound(rBound, lBound)) {
192                                                return false;
193                                        }
194                                }
195                        }
196                }
197                else {
198                        for (Type lBound : lUpperBounds) {
199                                if (!isAssignableBound(lBound, rhsType)) {
200                                        return false;
201                                }
202                        }
203
204                        for (Type lBound : lLowerBounds) {
205                                if (!isAssignableBound(rhsType, lBound)) {
206                                        return false;
207                                }
208                        }
209                }
210
211                return true;
212        }
213
214        public static boolean isAssignableBound(Type lhsType, Type rhsType) {
215                if (rhsType == null) {
216                        return true;
217                }
218                if (lhsType == null) {
219                        return false;
220                }
221                return isAssignable(lhsType, rhsType);
222        }
223
224}