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