1
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
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
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
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
92 ClassTypeInformation(Class<S> type) {
93 super(ProxyUtils.getUserClass(type), getTypeVariableMap(type));
94 this.type = type;
95 }
96
97
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
140 @Override
141 public Class<S> getType() {
142 return type;
143 }
144
145
149 @Override
150 public ClassTypeInformation<?> getRawTypeInformation() {
151 return this;
152 }
153
154
158 @Override
159 public boolean isAssignableFrom(TypeInformation<?> target) {
160 return getType().isAssignableFrom(target.getType());
161 }
162
163
167 @Override
168 public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
169 return (TypeInformation<? extends S>) type;
170 }
171
172
176 @Override
177 public String toString() {
178 return type.getName();
179 }
180 }
181