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