1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.scripting.thymeleaf;
17
18 import java.beans.BeanInfo;
19 import java.beans.IntrospectionException;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.Set;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29
30
31
32
33
34
35
36
37
38
39 public interface PropertyAccessor {
40
41
42
43
44
45
46
47
48
49 Set<String> getPropertyNames(Class<?> type);
50
51
52
53
54
55
56
57
58
59
60
61 Class<?> getPropertyType(Class<?> type, String name);
62
63
64
65
66
67
68
69
70
71
72
73 Object getPropertyValue(Object target, String name);
74
75
76
77
78
79
80
81
82
83
84
85 void setPropertyValue(Object target, String name, Object value);
86
87
88
89
90 enum BuiltIn implements PropertyAccessor {
91
92
93
94
95 STANDARD(new StandardPropertyAccessor());
96
97 private final PropertyAccessor delegate;
98
99 BuiltIn(PropertyAccessor delegate) {
100 this.delegate = delegate;
101 }
102
103
104
105
106 @Override
107 public Set<String> getPropertyNames(Class<?> type) {
108 return delegate.getPropertyNames(type);
109 }
110
111
112
113
114 @Override
115 public Class<?> getPropertyType(Class<?> type, String name) {
116 return delegate.getPropertyType(type, name);
117 }
118
119
120
121
122 @Override
123 public Object getPropertyValue(Object target, String name) {
124 return delegate.getPropertyValue(target, name);
125 }
126
127
128
129
130 @Override
131 public void setPropertyValue(Object target, String name, Object value) {
132 delegate.setPropertyValue(target, name, value);
133 }
134
135 static class StandardPropertyAccessor implements PropertyAccessor {
136
137 private static Map<Class<?>, Map<String, PropertyDescriptor>> cache = new ConcurrentHashMap<>();
138
139
140
141
142 @Override
143 public Set<String> getPropertyNames(Class<?> type) {
144 return getPropertyDescriptors(type).keySet();
145 }
146
147
148
149
150 @Override
151 public Class<?> getPropertyType(Class<?> type, String name) {
152 return Optional.ofNullable(getPropertyDescriptors(type).get(name))
153 .orElseThrow(() -> new IllegalArgumentException(String.format(
154 "Does not get a property type because property '%s' not found on '%s' class.", name, type.getName())))
155 .getPropertyType();
156 }
157
158
159
160
161 @Override
162 public Object getPropertyValue(Object target, String name) {
163 try {
164 return Optional.ofNullable(getPropertyDescriptors(target.getClass()).get(name))
165 .map(PropertyDescriptor::getReadMethod)
166 .orElseThrow(() -> new IllegalArgumentException(
167 String.format("Does not get a property value because property '%s' not found on '%s' class.", name,
168 target.getClass().getName())))
169 .invoke(target);
170 } catch (IllegalAccessException | InvocationTargetException e) {
171 throw new IllegalStateException(e);
172 }
173 }
174
175
176
177
178 @Override
179 public void setPropertyValue(Object target, String name, Object value) {
180 try {
181 Optional.ofNullable(getPropertyDescriptors(target.getClass()).get(name))
182 .map(PropertyDescriptor::getWriteMethod)
183 .orElseThrow(() -> new IllegalArgumentException(
184 String.format("Does not set a property value because property '%s' not found on '%s' class.", name,
185 target.getClass().getName())))
186 .invoke(target, value);
187 } catch (IllegalAccessException | InvocationTargetException e) {
188 throw new IllegalStateException(e);
189 }
190 }
191
192
193
194
195
196
197
198 static void clearCache() {
199 cache.clear();
200 }
201
202 private static Map<String, PropertyDescriptor> getPropertyDescriptors(Class<?> type) {
203 return cache.computeIfAbsent(type, key -> {
204 try {
205 BeanInfo beanInfo = Introspector.getBeanInfo(type);
206 return Stream.of(beanInfo.getPropertyDescriptors()).filter(x -> !x.getName().equals("class"))
207 .collect(Collectors.toMap(PropertyDescriptor::getName, v -> v));
208 } catch (IntrospectionException e) {
209 throw new IllegalStateException(e);
210 } finally {
211 Introspector.flushFromCaches(type);
212 }
213 });
214 }
215
216 }
217
218 }
219
220 }