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.mapping.model;
17
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Date;
21 import java.util.HashSet;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import java.util.WeakHashMap;
27
28 import org.springframework.util.Assert;
29
30 /**
31  * Simple container to hold a set of types to be considered simple types.
32  *
33  * @author Oliver Gierke
34  * @author Christoph Strobl
35  * @author Mark Paluch
36  */

37 public class SimpleTypeHolder {
38
39     private static final Set<Class<?>> DEFAULTS = new HashSet<Class<?>>() {
40
41         private static final long serialVersionUID = -1738594126505221500L;
42
43         {
44             add(boolean.class);
45             add(boolean[].class);
46             add(long.class);
47             add(long[].class);
48             add(short.class);
49             add(short[].class);
50             add(int.class);
51             add(int[].class);
52             add(byte.class);
53             add(byte[].class);
54             add(float.class);
55             add(float[].class);
56             add(double.class);
57             add(double[].class);
58             add(char.class);
59             add(char[].class);
60             add(Boolean.class);
61             add(Long.class);
62             add(Short.class);
63             add(Integer.class);
64             add(Byte.class);
65             add(Float.class);
66             add(Double.class);
67             add(Character.class);
68             add(String.class);
69             add(Date.class);
70             add(Locale.class);
71             add(Class.class);
72             add(Enum.class);
73         }
74     };
75     public static final SimpleTypeHolder DEFAULT = new SimpleTypeHolder();
76
77     private volatile Map<Class<?>, Boolean> simpleTypes;
78
79     /**
80      * Creates a new {@link SimpleTypeHolder} containing the default types.
81      *
82      * @see #SimpleTypeHolder(Set, boolean)
83      */

84     protected SimpleTypeHolder() {
85         this(Collections.emptySet(), true);
86     }
87
88     /**
89      * Creates a new {@link SimpleTypeHolder} to carry the given custom simple types. Registration of default simple types
90      * can be deactivated by passing {@literal falsefor {@code registerDefaults}.
91      *
92      * @param customSimpleTypes
93      * @param registerDefaults
94      */

95     public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, boolean registerDefaults) {
96
97         Assert.notNull(customSimpleTypes, "CustomSimpleTypes must not be null!");
98
99         this.simpleTypes = new WeakHashMap<>(customSimpleTypes.size() + DEFAULTS.size());
100
101         register(customSimpleTypes);
102
103         if (registerDefaults) {
104             register(DEFAULTS);
105         }
106     }
107
108     /**
109      * Copy constructor to create a new {@link SimpleTypeHolder} that carries the given additional custom simple types.
110      *
111      * @param customSimpleTypes must not be {@literal null}
112      * @param source must not be {@literal null}
113      */

114     public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, SimpleTypeHolder source) {
115
116         Assert.notNull(customSimpleTypes, "CustomSimpleTypes must not be null!");
117         Assert.notNull(source, "SourceTypeHolder must not be null!");
118
119         this.simpleTypes = new WeakHashMap<>(customSimpleTypes.size() + source.simpleTypes.size());
120
121         register(customSimpleTypes);
122         registerCachePositives(source.simpleTypes);
123     }
124
125     private void registerCachePositives(Map<Class<?>, Boolean> source) {
126
127         for (Entry<Class<?>, Boolean> entry : source.entrySet()) {
128
129             if (!entry.getValue()) {
130                 continue;
131             }
132
133             this.simpleTypes.put(entry.getKey(), true);
134         }
135     }
136
137     /**
138      * Returns whether the given type is considered a simple one.
139      *
140      * @param type must not be {@literal null}.
141      * @return
142      */

143     public boolean isSimpleType(Class<?> type) {
144
145         Assert.notNull(type, "Type must not be null!");
146
147         Map<Class<?>, Boolean> localSimpleTypes = this.simpleTypes;
148         Boolean isSimpleType = localSimpleTypes.get(type);
149
150         if (Object.class.equals(type) || Enum.class.isAssignableFrom(type)) {
151             return true;
152         }
153
154         if (isSimpleType != null) {
155             return isSimpleType;
156         }
157
158         String typeName = type.getName();
159
160         if (typeName.startsWith("java.lang") || type.getName().startsWith("java.time") || typeName.equals("kotlin.Unit")) {
161             return true;
162         }
163
164         for (Class<?> simpleType : localSimpleTypes.keySet()) {
165
166             if (simpleType.isAssignableFrom(type)) {
167
168                 isSimpleType = localSimpleTypes.get(simpleType);
169                 this.simpleTypes = put(localSimpleTypes, type, isSimpleType);
170                 return isSimpleType;
171             }
172         }
173
174         this.simpleTypes = put(localSimpleTypes, type, false);
175
176         return false;
177     }
178
179     private void register(Collection<? extends Class<?>> types) {
180         types.forEach(customSimpleType -> this.simpleTypes.put(customSimpleType, true));
181     }
182
183     private static Map<Class<?>, Boolean> put(Map<Class<?>, Boolean> simpleTypes, Class<?> type, boolean isSimpleType) {
184
185         Map<Class<?>, Boolean> copy = new WeakHashMap<>(simpleTypes);
186         copy.put(type, isSimpleType);
187
188         return copy;
189     }
190 }
191