1 /*
2  * Copyright 2011-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.data.util;
17
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Type;
20 import java.lang.reflect.TypeVariable;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Set;
30
31 import org.springframework.core.GenericTypeResolver;
32 import org.springframework.util.Assert;
33 import org.springframework.util.ConcurrentReferenceHashMap;
34 import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
35
36 /**
37  * {@link TypeInformation} for a plain {@link Class}.
38  *
39  * @author Oliver Gierke
40  * @author Christoph Strobl
41  */

42 @SuppressWarnings({ "unchecked""rawtypes" })
43 public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
44
45     public static final ClassTypeInformation<Collection> COLLECTION = new ClassTypeInformation(Collection.class);
46     public static final ClassTypeInformation<List> LIST = new ClassTypeInformation(List.class);
47     public static final ClassTypeInformation<Set> SET = new ClassTypeInformation(Set.class);
48     public static final ClassTypeInformation<Map> MAP = new ClassTypeInformation(Map.class);
49     public static final ClassTypeInformation<Object> OBJECT = new ClassTypeInformation(Object.class);
50
51     private static final Map<Class<?>, ClassTypeInformation<?>> CACHE = new ConcurrentReferenceHashMap<>(64,
52             ReferenceType.WEAK);
53
54     static {
55         Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> CACHE.put(it.getType(), it));
56     }
57
58     private final Class<S> type;
59
60     /**
61      * Simple factory method to easily create new instances of {@link ClassTypeInformation}.
62      *
63      * @param <S>
64      * @param type must not be {@literal null}.
65      * @return
66      */

67     public static <S> ClassTypeInformation<S> from(Class<S> type) {
68
69         Assert.notNull(type, "Type must not be null!");
70
71         return (ClassTypeInformation<S>) CACHE.computeIfAbsent(type, ClassTypeInformation::new);
72     }
73
74     /**
75      * Creates a {@link TypeInformation} from the given method's return type.
76      *
77      * @param method must not be {@literal null}.
78      * @return
79      */

80     public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
81
82         Assert.notNull(method, "Method must not be null!");
83         return (TypeInformation<S>) ClassTypeInformation.from(method.getDeclaringClass())
84                 .createInfo(method.getGenericReturnType());
85     }
86
87     /**
88      * Creates {@link ClassTypeInformation} for the given type.
89      *
90      * @param type
91      */

92     ClassTypeInformation(Class<S> type) {
93         super(ProxyUtils.getUserClass(type), getTypeVariableMap(type));
94         this.type = type;
95     }
96
97     /**
98      * Little helper to allow us to create a generified map, actually just to satisfy the compiler.
99      *
100      * @param type must not be {@literal null}.
101      * @return
102      */

103     private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type) {
104         return getTypeVariableMap(type, new HashSet<>());
105     }
106
107     private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type, Collection<Type> visited) {
108
109         if (visited.contains(type)) {
110             return Collections.emptyMap();
111         } else {
112             visited.add(type);
113         }
114
115         Map<TypeVariable, Type> source = GenericTypeResolver.getTypeVariableMap(type);
116         Map<TypeVariable<?>, Type> map = new HashMap<>(source.size());
117
118         for (Entry<TypeVariable, Type> entry : source.entrySet()) {
119
120             Type value = entry.getValue();
121             map.put(entry.getKey(), entry.getValue());
122
123             if (value instanceof Class) {
124
125                 for (Entry<TypeVariable<?>, Type> nestedEntry : getTypeVariableMap((Class<?>) value, visited).entrySet()) {
126                     if (!map.containsKey(nestedEntry.getKey())) {
127                         map.put(nestedEntry.getKey(), nestedEntry.getValue());
128                     }
129                 }
130             }
131         }
132
133         return map;
134     }
135
136     /*
137      * (non-Javadoc)
138      * @see org.springframework.data.util.TypeDiscoverer#getType()
139      */

140     @Override
141     public Class<S> getType() {
142         return type;
143     }
144
145     /*
146      * (non-Javadoc)
147      * @see org.springframework.data.util.TypeDiscoverer#getRawTypeInformation()
148      */

149     @Override
150     public ClassTypeInformation<?> getRawTypeInformation() {
151         return this;
152     }
153
154     /*
155      * (non-Javadoc)
156      * @see org.springframework.data.util.TypeDiscoverer#isAssignableFrom(org.springframework.data.util.TypeInformation)
157      */

158     @Override
159     public boolean isAssignableFrom(TypeInformation<?> target) {
160         return getType().isAssignableFrom(target.getType());
161     }
162
163     /*
164      * (non-Javadoc)
165      * @see org.springframework.data.util.TypeDiscoverer#specialize(org.springframework.data.util.ClassTypeInformation)
166      */

167     @Override
168     public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
169         return (TypeInformation<? extends S>) type;
170     }
171
172     /*
173      * (non-Javadoc)
174      * @see java.lang.Object#toString()
175      */

176     @Override
177     public String toString() {
178         return type.getName();
179     }
180 }
181