1
16 package org.springframework.data.spel;
17
18 import static org.springframework.data.util.StreamUtils.*;
19
20 import lombok.Getter;
21 import lombok.RequiredArgsConstructor;
22
23 import java.beans.PropertyDescriptor;
24 import java.lang.reflect.Field;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.Optional;
34
35 import org.springframework.beans.BeanUtils;
36 import org.springframework.data.spel.EvaluationContextExtensionInformation.ExtensionTypeInformation.PublicMethodAndFieldFilter;
37 import org.springframework.data.spel.spi.EvaluationContextExtension;
38 import org.springframework.data.spel.spi.Function;
39 import org.springframework.data.util.Streamable;
40 import org.springframework.util.Assert;
41 import org.springframework.util.CollectionUtils;
42 import org.springframework.util.LinkedMultiValueMap;
43 import org.springframework.util.MultiValueMap;
44 import org.springframework.util.ReflectionUtils;
45 import org.springframework.util.ReflectionUtils.FieldFilter;
46 import org.springframework.util.ReflectionUtils.MethodFilter;
47
48
62 class EvaluationContextExtensionInformation {
63
64 private final ExtensionTypeInformation extensionTypeInformation;
65 private final Optional<RootObjectInformation> rootObjectInformation;
66
67
72 public EvaluationContextExtensionInformation(Class<? extends EvaluationContextExtension> type) {
73
74 Assert.notNull(type, "Extension type must not be null!");
75
76 Class<?> rootObjectType = org.springframework.data.util.ReflectionUtils.findRequiredMethod(type, "getRootObject")
77 .getReturnType();
78
79 this.rootObjectInformation = Optional
80 .ofNullable(Object.class.equals(rootObjectType) ? null : new RootObjectInformation(rootObjectType));
81 this.extensionTypeInformation = new ExtensionTypeInformation(type);
82 }
83
84
89 public ExtensionTypeInformation getExtensionTypeInformation() {
90 return this.extensionTypeInformation;
91 }
92
93
100 public RootObjectInformation getRootObjectInformation(Optional<Object> target) {
101
102 return target.map(it -> rootObjectInformation.orElseGet(() -> new RootObjectInformation(it.getClass())))
103 .orElse(RootObjectInformation.NONE);
104 }
105
106
112 @Getter
113 public static class ExtensionTypeInformation {
114
115
120 private final Map<String, Object> properties;
121
122
127 private final MultiValueMap<String, Function> functions;
128
129
134 public ExtensionTypeInformation(Class<? extends EvaluationContextExtension> type) {
135
136 Assert.notNull(type, "Extension type must not be null!");
137
138 this.functions = discoverDeclaredFunctions(type);
139 this.properties = discoverDeclaredProperties(type);
140 }
141
142 private static MultiValueMap<String, Function> discoverDeclaredFunctions(Class<?> type) {
143
144 MultiValueMap<String, Function> map = CollectionUtils.toMultiValueMap(new HashMap<>());
145
146 ReflectionUtils.doWithMethods(type,
147 method -> map.add(method.getName(), new Function(method, null)),
148 PublicMethodAndFieldFilter.STATIC);
149
150 return CollectionUtils.unmodifiableMultiValueMap(map);
151 }
152
153 @RequiredArgsConstructor
154 static class PublicMethodAndFieldFilter implements MethodFilter, FieldFilter {
155
156 public static final PublicMethodAndFieldFilter STATIC = new PublicMethodAndFieldFilter(true);
157 public static final PublicMethodAndFieldFilter NON_STATIC = new PublicMethodAndFieldFilter(false);
158
159 private final boolean staticOnly;
160
161
165 @Override
166 public boolean matches(Method method) {
167
168 if (ReflectionUtils.isObjectMethod(method)) {
169 return false;
170 }
171
172 boolean methodStatic = Modifier.isStatic(method.getModifiers());
173 boolean staticMatch = staticOnly ? methodStatic : !methodStatic;
174
175 return Modifier.isPublic(method.getModifiers()) && staticMatch;
176 }
177
178
182 @Override
183 public boolean matches(Field field) {
184
185 boolean fieldStatic = Modifier.isStatic(field.getModifiers());
186 boolean staticMatch = staticOnly ? fieldStatic : !fieldStatic;
187
188 return Modifier.isPublic(field.getModifiers()) && staticMatch;
189 }
190 }
191 }
192
193
198 static class RootObjectInformation {
199
200 private static final RootObjectInformation NONE = new RootObjectInformation(Object.class);
201
202 private final Map<String, Method> accessors;
203 private final Collection<Method> methods;
204 private final Collection<Field> fields;
205
206
212 public RootObjectInformation(Class<?> type) {
213
214 Assert.notNull(type, "Type must not be null!");
215
216 this.accessors = new HashMap<>();
217 this.methods = new HashSet<>();
218 this.fields = new ArrayList<>();
219
220 if (Object.class.equals(type)) {
221 return;
222 }
223
224 Streamable<PropertyDescriptor> descriptors = Streamable.of(BeanUtils.getPropertyDescriptors(type));
225
226 ReflectionUtils.doWithMethods(type, method -> {
227
228 RootObjectInformation.this.methods.add(method);
229
230 descriptors.stream()
231 .filter(it -> method.equals(it.getReadMethod()))
232 .forEach(it -> RootObjectInformation.this.accessors.put(it.getName(), method));
233
234 }, PublicMethodAndFieldFilter.NON_STATIC);
235
236 ReflectionUtils.doWithFields(type, RootObjectInformation.this.fields::add, PublicMethodAndFieldFilter.NON_STATIC);
237 }
238
239
245 public MultiValueMap<String, Function> getFunctions(Optional<Object> target) {
246 return target.map(this::getFunctions).orElseGet(() -> new LinkedMultiValueMap<>());
247 }
248
249 private MultiValueMap<String, Function> getFunctions(Object target) {
250 return methods.stream().collect(toMultiMap(Method::getName, m -> new Function(m, target)));
251 }
252
253
259 public Map<String, Object> getProperties(Optional<Object> target) {
260
261 return target.map(it -> {
262
263 Map<String, Object> properties = new HashMap<>();
264
265 accessors.entrySet().stream()
266 .forEach(method -> properties.put(method.getKey(), new Function(method.getValue(), it)));
267 fields.stream().forEach(field -> properties.put(field.getName(), ReflectionUtils.getField(field, it)));
268
269 return Collections.unmodifiableMap(properties);
270
271 }).orElseGet(Collections::emptyMap);
272 }
273 }
274
275 private static Map<String, Object> discoverDeclaredProperties(Class<?> type) {
276
277 Map<String, Object> map = new HashMap<>();
278
279 ReflectionUtils.doWithFields(type, field -> map.put(field.getName(), field.get(null)),
280 PublicMethodAndFieldFilter.STATIC);
281
282 return map.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(map);
283 }
284 }
285