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