1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.jexl342;
18
19 import java.lang.ref.Reference;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import org.apache.commons.jexl3.JexlArithmetic;
30 import org.apache.commons.jexl3.JexlEngine;
31 import org.apache.commons.jexl3.JexlException;
32 import org.apache.commons.jexl3.JexlOperator;
33 import org.apache.commons.jexl3.introspection.JexlMethod;
34 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
35 import org.apache.commons.jexl3.introspection.JexlPropertySet;
36 import org.apache.commons.jexl3.introspection.JexlUberspect;
37
38
39
40
41
42
43 public class ReferenceUberspect implements JexlUberspect {
44
45
46
47
48 @FunctionalInterface
49 interface ReferenceHandler {
50
51
52
53
54
55
56 Object callGet(Object ref);
57 }
58
59
60 private static final Object[] EMPTY_PARAMS = {};
61
62
63
64
65
66
67
68
69
70
71 private static JexlPropertyGet discoverFind(final JexlUberspect is, final Class<?> clazz, final String property) {
72 if (property == null || property.isEmpty()) {
73 return null;
74 }
75
76 JexlMethod method;
77 final int start = 4;
78
79 final StringBuilder sb = new StringBuilder("find");
80 sb.append(property);
81
82 final char c = sb.charAt(start);
83 sb.setCharAt(start, Character.toUpperCase(c));
84 method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
85
86 if (method == null) {
87 sb.setCharAt(start, Character.toLowerCase(c));
88 method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
89 }
90 if (method != null && Optional.class.equals(method.getReturnType())) {
91 final JexlMethod getter = method;
92 final String name = sb.toString();
93 return new JexlPropertyGet() {
94 @Override
95 public Object invoke(final Object obj) throws Exception {
96 return getter.invoke(obj);
97 }
98
99 @Override
100 public boolean isCacheable() {
101 return getter.isCacheable();
102 }
103
104 @Override
105 public boolean tryFailed(final Object rval) {
106 return rval == JexlEngine.TRY_FAILED;
107 }
108
109 @Override
110 public Object tryInvoke(final Object obj, final Object key) throws JexlException.TryFailed {
111 return !Objects.equals(property, key) ? JexlEngine.TRY_FAILED : getter.tryInvoke(name, obj);
112 }
113 };
114 }
115 return null;
116 }
117
118
119
120
121
122
123 private static ReferenceHandler discoverHandler(final Object ref) {
124
125 if (ref instanceof Optional<?>) {
126 return ReferenceUberspect::handleOptional;
127 }
128
129 if (ref instanceof AtomicReference<?>) {
130 return ReferenceUberspect::handleAtomic;
131 }
132
133 if (ref instanceof Reference<?>) {
134 return ReferenceUberspect::handleReference;
135 }
136
137 return null;
138 }
139
140
141
142
143
144
145 static Object handleAtomic(final Object ref) {
146 final Object obj = ((AtomicReference<?>) ref).get();
147 return obj == null ? ref : obj;
148 }
149
150
151
152
153
154
155 static Object handleOptional(final Object ref) {
156
157 final Optional<?> optional = (Optional<?>) ref;
158 return optional.isPresent() ? optional.get() : null;
159 }
160
161
162
163
164
165
166 static Object handleReference(final Object ref) {
167 final Object obj = ((Reference<?>) ref).get();
168 return obj == null ? ref : obj;
169 }
170
171
172 private final JexlUberspect uberspect;
173
174
175
176
177 private final List<PropertyResolver> pojoStrategy;
178
179
180
181
182 private final List<PropertyResolver> mapStrategy;
183
184
185
186
187
188 public ReferenceUberspect(final JexlUberspect jexlUberspect) {
189 uberspect = jexlUberspect;
190 final PropertyResolver find = new PropertyResolver() {
191 @Override
192 public JexlPropertyGet getPropertyGet(final JexlUberspect uber, final Object obj, final Object identifier) {
193 return discoverFind(uberspect, obj.getClass(), identifier.toString());
194 }
195
196 @Override
197 public JexlPropertySet getPropertySet(final JexlUberspect uber, final Object obj, final Object identifier, final Object arg) {
198 return null;
199 }
200 };
201 pojoStrategy = Arrays.asList(
202 JexlResolver.PROPERTY,
203 find,
204 JexlResolver.MAP,
205 JexlResolver.LIST,
206 JexlResolver.DUCK,
207 JexlResolver.FIELD,
208 JexlResolver.CONTAINER);
209 mapStrategy = Arrays.asList(
210 JexlResolver.MAP,
211 JexlResolver.LIST,
212 JexlResolver.DUCK,
213 JexlResolver.PROPERTY,
214 find,
215 JexlResolver.FIELD,
216 JexlResolver.CONTAINER);
217 }
218
219 @Override
220 public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
221 return getOperator(arithmetic);
222 }
223
224 @Override
225 public JexlOperator.Uberspect getOperator(final JexlArithmetic arithmetic) {
226 return uberspect.getOperator(arithmetic);
227 }
228
229 @Override
230 public Class<?> getClassByName(final String className) {
231 return uberspect.getClassByName(className);
232 }
233
234 @Override
235 public ClassLoader getClassLoader() {
236 return uberspect.getClassLoader();
237 }
238
239 @Override
240 public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
241 return uberspect.getConstructor(ctorHandle, args);
242 }
243
244 @Override
245 public Iterator<?> getIterator(final Object ref) {
246
247 final ReferenceHandler handler = discoverHandler(ref);
248 if (handler == null) {
249 return uberspect.getIterator(ref);
250 }
251
252 final Object obj = handler.callGet(ref);
253 if (ref == obj) {
254 return null;
255 }
256 if (obj == null) {
257 return Collections.emptyIterator();
258 }
259 return uberspect.getIterator(obj);
260 }
261
262 @Override
263 public JexlMethod getMethod(final Object ref, final String method, final Object... args) {
264
265 final ReferenceHandler handler = discoverHandler(ref);
266 if (handler == null) {
267 return uberspect.getMethod(ref, method, args);
268 }
269
270 final Object obj = handler.callGet(ref);
271 if (ref == obj) {
272 return null;
273 }
274 JexlMethod jexlMethod = null;
275 if (obj != null) {
276 jexlMethod = uberspect.getMethod(obj, method, args);
277 if (jexlMethod == null) {
278 throw new JexlException.Method(null, method, args, null);
279 }
280 } else {
281 jexlMethod = new OptionalNullMethod(uberspect, method);
282 }
283 return new ReferenceMethodExecutor(handler, jexlMethod);
284 }
285
286 @Override
287 public JexlPropertyGet getPropertyGet(final List<PropertyResolver> resolvers, final Object ref, final Object identifier) {
288
289 final ReferenceHandler handler = discoverHandler(ref);
290 if (handler == null) {
291 return uberspect.getPropertyGet(resolvers, ref, identifier);
292 }
293
294 final Object obj = handler.callGet(ref);
295 if (ref == obj) {
296 return null;
297 }
298
299
300
301
302 JexlPropertyGet jexlGet = null;
303 if (obj != null) {
304 jexlGet = uberspect.getPropertyGet(resolvers, obj, identifier);
305 if (jexlGet == null) {
306 throw new JexlException.Property(null, Objects.toString(identifier), false, null);
307 }
308 } else {
309 jexlGet = new OptionalNullGetter(uberspect, identifier);
310 }
311 return new ReferenceGetExecutor(handler, jexlGet);
312 }
313
314 @Override
315 public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
316 return getPropertyGet(null, obj, identifier);
317 }
318
319 @Override
320 public JexlPropertySet getPropertySet(final List<PropertyResolver> resolvers, final Object ref, final Object identifier, final Object arg) {
321
322 final ReferenceHandler handler = discoverHandler(ref);
323 if (handler == null) {
324 return uberspect.getPropertySet(resolvers, ref, identifier, arg);
325 }
326
327 final Object obj = handler.callGet(ref);
328 if (ref == obj) {
329 return null;
330 }
331
332 JexlPropertySet jexlSet = null;
333 if (obj != null) {
334 jexlSet = uberspect.getPropertySet(resolvers, obj, identifier, arg);
335 if (jexlSet == null) {
336 throw new JexlException.Property(null, Objects.toString(identifier), false, null);
337 }
338 } else {
339
340 jexlSet = new OptionalNullSetter(uberspect, identifier);
341 }
342 return new ReferenceSetExecutor(handler, jexlSet);
343 }
344
345 @Override
346 public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
347 return getPropertySet(null, obj, identifier, arg);
348 }
349
350
351
352
353 @Override
354 public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
355 if (op == JexlOperator.ARRAY_GET) {
356 return mapStrategy;
357 }
358 if (op == JexlOperator.ARRAY_SET) {
359 return mapStrategy;
360 }
361 if (op == null && obj instanceof Map) {
362 return mapStrategy;
363 }
364 return pojoStrategy;
365 }
366
367 @Override
368 public int getVersion() {
369 return uberspect.getVersion();
370 }
371
372 @Override
373 public void setClassLoader(final ClassLoader loader) {
374 uberspect.setClassLoader(loader);
375 }
376
377 }