1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal.introspection;
18
19 import org.apache.commons.jexl3.JexlArithmetic;
20 import org.apache.commons.jexl3.JexlEngine;
21 import org.apache.commons.jexl3.JexlOperator;
22 import org.apache.commons.jexl3.introspection.JexlMethod;
23 import org.apache.commons.jexl3.introspection.JexlPermissions;
24 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
25 import org.apache.commons.jexl3.introspection.JexlPropertySet;
26 import org.apache.commons.jexl3.introspection.JexlUberspect;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33
34 import java.util.EnumSet;
35 import java.util.Enumeration;
36 import java.util.Iterator;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.concurrent.atomic.AtomicInteger;
40 import java.util.concurrent.ConcurrentHashMap;
41
42 import java.lang.ref.Reference;
43 import java.lang.ref.SoftReference;
44 import java.util.List;
45
46
47
48
49
50
51
52
53
54 public class Uberspect implements JexlUberspect {
55
56 public static final Object TRY_FAILED = JexlEngine.TRY_FAILED;
57
58 protected final Log logger;
59
60 private final JexlUberspect.ResolverStrategy strategy;
61
62 private final JexlPermissions permissions;
63
64 private final AtomicInteger version;
65
66 private volatile Reference<Introspector> ref;
67
68 private volatile Reference<ClassLoader> loader;
69
70
71
72
73
74
75 private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap;
76
77
78
79
80
81
82 public Uberspect(final Log runtimeLogger, final JexlUberspect.ResolverStrategy sty) {
83 this(runtimeLogger, sty, null);
84 }
85
86
87
88
89
90
91
92 public Uberspect(final Log runtimeLogger, final JexlUberspect.ResolverStrategy sty, final JexlPermissions perms) {
93 logger = runtimeLogger == null? LogFactory.getLog(JexlEngine.class) : runtimeLogger;
94 strategy = sty == null? JexlUberspect.JEXL_STRATEGY : sty;
95 permissions = perms == null? JexlPermissions.RESTRICTED : perms;
96 ref = new SoftReference<>(null);
97 loader = new SoftReference<>(getClass().getClassLoader());
98 operatorMap = new ConcurrentHashMap<>();
99 version = new AtomicInteger(0);
100 }
101
102
103
104
105
106
107
108
109 protected final Introspector base() {
110 Introspector intro = ref.get();
111 if (intro == null) {
112
113 synchronized (this) {
114 intro = ref.get();
115 if (intro == null) {
116 intro = new Introspector(logger, loader.get(), permissions);
117 ref = new SoftReference<>(intro);
118 loader = new SoftReference<>(intro.getLoader());
119 version.incrementAndGet();
120 }
121 }
122 }
123 return intro;
124 }
125
126
127 @Override
128 public void setClassLoader(final ClassLoader nloader) {
129 synchronized (this) {
130 Introspector intro = ref.get();
131 if (intro != null) {
132 intro.setLoader(nloader);
133 } else {
134 intro = new Introspector(logger, nloader, permissions);
135 ref = new SoftReference<>(intro);
136 }
137 loader = new SoftReference<>(intro.getLoader());
138 operatorMap.clear();
139 version.incrementAndGet();
140 }
141 }
142
143 @Override
144 public ClassLoader getClassLoader() {
145 return loader.get();
146 }
147
148 @Override
149 public int getVersion() {
150 return version.intValue();
151 }
152
153
154
155
156
157
158 @Override
159 public final Class<?> getClassByName(final String className) {
160 return base().getClassByName(className);
161 }
162
163
164
165
166
167
168
169
170
171
172 public final Field getField(final Class<?> c, final String key) {
173 return base().getField(c, key);
174 }
175
176
177
178
179
180
181 public final String[] getFieldNames(final Class<?> c) {
182 return base().getFieldNames(c);
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 public final Method getMethod(final Class<?> c, final String name, final Object[] params) {
199 return base().getMethod(c, new MethodKey(name, params));
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213 public final Method getMethod(final Class<?> c, final MethodKey key) {
214 return base().getMethod(c, key);
215 }
216
217
218
219
220
221
222 public final String[] getMethodNames(final Class<?> c) {
223 return base().getMethodNames(c);
224 }
225
226
227
228
229
230
231
232 public final Method[] getMethods(final Class<?> c, final String methodName) {
233 return base().getMethods(c, methodName);
234 }
235
236 @Override
237 public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
238 return MethodExecutor.discover(base(), obj, method, args);
239 }
240
241 @Override
242 public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
243 return strategy.apply(op, obj);
244 }
245
246 @Override
247 public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
248 return getPropertyGet(null, obj, identifier);
249 }
250
251 @Override
252 public JexlPropertyGet getPropertyGet(
253 final List<PropertyResolver> resolvers, final Object obj, final Object identifier
254 ) {
255 final Class<?> claz = obj.getClass();
256 final String property = AbstractExecutor.castString(identifier);
257 final Introspector is = base();
258 final List<PropertyResolver> r = resolvers == null? strategy.apply(null, obj) : resolvers;
259 JexlPropertyGet executor = null;
260 for (final PropertyResolver resolver : r) {
261 if (resolver instanceof JexlResolver) {
262 switch ((JexlResolver) resolver) {
263 case PROPERTY:
264
265 executor = PropertyGetExecutor.discover(is, claz, property);
266 if (executor == null) {
267 executor = BooleanGetExecutor.discover(is, claz, property);
268 }
269 break;
270 case MAP:
271
272 executor = MapGetExecutor.discover(is, claz, identifier);
273 break;
274 case LIST:
275
276 final Integer index = AbstractExecutor.castInteger(identifier);
277 if (index != null) {
278 executor = ListGetExecutor.discover(is, claz, index);
279 }
280 break;
281 case DUCK:
282
283 executor = DuckGetExecutor.discover(is, claz, identifier);
284 if (executor == null && property != null && property != identifier) {
285
286 executor = DuckGetExecutor.discover(is, claz, property);
287 }
288 break;
289 case FIELD:
290
291 executor = FieldGetExecutor.discover(is, claz, property);
292
293 if (obj instanceof Class<?>) {
294 executor = FieldGetExecutor.discover(is, (Class<?>) obj, property);
295 }
296 break;
297 case CONTAINER:
298
299 executor = IndexedType.discover(is, obj, property);
300 break;
301 default:
302 continue;
303 }
304 } else {
305 executor = resolver.getPropertyGet(this, obj, identifier);
306 }
307 if (executor != null) {
308 return executor;
309 }
310 }
311 return null;
312 }
313
314 @Override
315 public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
316 return getPropertySet(null, obj, identifier, arg);
317 }
318
319 @Override
320 public JexlPropertySet getPropertySet(
321 final List<PropertyResolver> resolvers, final Object obj, final Object identifier, final Object arg
322 ) {
323 final Class<?> claz = obj.getClass();
324 final String property = AbstractExecutor.castString(identifier);
325 final Introspector is = base();
326 final List<PropertyResolver> actual = resolvers == null? strategy.apply(null, obj) : resolvers;
327 JexlPropertySet executor = null;
328 for (final PropertyResolver resolver : actual) {
329 if (resolver instanceof JexlResolver) {
330 switch ((JexlResolver) resolver) {
331 case PROPERTY:
332
333 executor = PropertySetExecutor.discover(is, claz, property, arg);
334 break;
335 case MAP:
336
337 executor = MapSetExecutor.discover(is, claz, identifier, arg);
338 break;
339 case LIST:
340
341
342 final Integer index = AbstractExecutor.castInteger(identifier);
343 if (index != null) {
344 executor = ListSetExecutor.discover(is, claz, identifier, arg);
345 }
346 break;
347 case DUCK:
348
349 executor = DuckSetExecutor.discover(is, claz, identifier, arg);
350 if (executor == null && property != null && property != identifier) {
351 executor = DuckSetExecutor.discover(is, claz, property, arg);
352 }
353 break;
354 case FIELD:
355
356 executor = FieldSetExecutor.discover(is, claz, property, arg);
357 break;
358 case CONTAINER:
359 default:
360 continue;
361 }
362 } else {
363 executor = resolver.getPropertySet(this, obj, identifier, arg);
364 }
365 if (executor != null) {
366 return executor;
367 }
368 }
369 return null;
370 }
371
372 @Override
373 @SuppressWarnings("unchecked")
374 public Iterator<?> getIterator(final Object obj) {
375 if (!permissions.allow(obj.getClass())) {
376 return null;
377 }
378 if (obj instanceof Iterator<?>) {
379 return ((Iterator<?>) obj);
380 }
381 if (obj.getClass().isArray()) {
382 return new ArrayIterator(obj);
383 }
384 if (obj instanceof Map<?, ?>) {
385 return ((Map<?, ?>) obj).values().iterator();
386 }
387 if (obj instanceof Enumeration<?>) {
388 return new EnumerationIterator<>((Enumeration<Object>) obj);
389 }
390 if (obj instanceof Iterable<?>) {
391 return ((Iterable<?>) obj).iterator();
392 }
393 try {
394
395
396
397 final JexlMethod it = getMethod(obj, "iterator", (Object[]) null);
398 if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
399 return (Iterator<Object>) it.invoke(obj, (Object[]) null);
400 }
401 } catch (final Exception xany) {
402 if (logger != null && logger.isDebugEnabled()) {
403 logger.info("unable to solve iterator()", xany);
404 }
405 }
406 return null;
407 }
408
409 @Override
410 public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
411 return ConstructorMethod.discover(base(), ctorHandle, args);
412 }
413
414
415
416
417 protected class ArithmeticUberspect implements JexlArithmetic.Uberspect {
418
419 private final JexlArithmetic arithmetic;
420
421 private final Set<JexlOperator> overloads;
422
423
424
425
426
427
428 ArithmeticUberspect(final JexlArithmetic theArithmetic, final Set<JexlOperator> theOverloads) {
429 this.arithmetic = theArithmetic;
430 this.overloads = theOverloads;
431 }
432
433 @Override
434 public JexlMethod getOperator(final JexlOperator operator, final Object... args) {
435 return overloads.contains(operator) && args != null
436 ? getMethod(arithmetic, operator.getMethodName(), args)
437 : null;
438 }
439
440 @Override
441 public boolean overloads(final JexlOperator operator) {
442 return overloads.contains(operator);
443 }
444 }
445
446 @Override
447 public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
448 JexlArithmetic.Uberspect jau = null;
449 if (arithmetic != null) {
450 final Class<? extends JexlArithmetic> aclass = arithmetic.getClass();
451 Set<JexlOperator> ops = operatorMap.get(aclass);
452 if (ops == null) {
453 ops = EnumSet.noneOf(JexlOperator.class);
454
455 if (!JexlArithmetic.class.equals(aclass)) {
456 for (final JexlOperator op : JexlOperator.values()) {
457 final Method[] methods = getMethods(arithmetic.getClass(), op.getMethodName());
458 if (methods != null) {
459 for (final Method method : methods) {
460 final Class<?>[] parms = method.getParameterTypes();
461 if (parms.length != op.getArity()) {
462 continue;
463 }
464
465
466
467 if (!JexlArithmetic.class.equals(method.getDeclaringClass())) {
468 try {
469 JexlArithmetic.class.getMethod(method.getName(), method.getParameterTypes());
470 } catch (final NoSuchMethodException xmethod) {
471
472 ops.add(op);
473 }
474 }
475 }
476 }
477 }
478 }
479
480 operatorMap.put(aclass, ops);
481 }
482 jau = new ArithmeticUberspect(arithmetic, ops);
483 }
484 return jau;
485 }
486 }