1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.jexl3.introspection;
19
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
26 import org.apache.commons.jexl3.JexlArithmetic;
27 import org.apache.commons.jexl3.JexlOperator;
28
29 /**
30 * 'Federated' introspection/reflection interface to allow JEXL introspection
31 * behavior to be customized.
32 *
33 * @since 1.0
34 */
35 public interface JexlUberspect {
36 /**
37 * The various builtin property resolvers.
38 * <p>
39 * Each resolver discovers how to set/get a property with different techniques; seeking
40 * method names or field names, etc.
41 *
42 * @since 3.0
43 */
44 enum JexlResolver implements PropertyResolver {
45 /** Seeks methods named get{P,p}property and is{P,p}property. */
46 PROPERTY,
47
48 /** Seeks map methods get/put. */
49 MAP,
50
51 /** Seeks list methods get/set. */
52 LIST,
53
54 /** Seeks any get/{set,put} method (quacking like a list or a map). */
55 DUCK,
56
57 /** Seeks public instance members.*/
58 FIELD,
59
60 /** Seeks a getContainer(property) and setContainer(property, value) as in {@code x.container.property}. */
61 CONTAINER;
62
63 @Override
64 public final JexlPropertyGet getPropertyGet(final JexlUberspect uber,
65 final Object obj,
66 final Object identifier) {
67 return uber.getPropertyGet(Collections.singletonList(this), obj, identifier);
68 }
69
70 @Override
71 public final JexlPropertySet getPropertySet(final JexlUberspect uber,
72 final Object obj,
73 final Object identifier,
74 final Object arg) {
75 return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg);
76 }
77 }
78
79 /**
80 * Abstracts getting property setter and getter.
81 * <p>
82 * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
83 * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter
84 * stops the search.
85 *
86 * @see JexlResolver
87 * @see JexlUberspect#getPropertyGet
88 * @see JexlUberspect#getPropertySet
89 * @since 3.0
90 */
91 interface PropertyResolver {
92
93 /**
94 * Gets a property getter.
95 *
96 * @param uber the uberspect
97 * @param obj the object
98 * @param identifier the property identifier
99 * @return the property getter or null
100 */
101 JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier);
102
103 /**
104 * Gets a property setter.
105 *
106 * @param uber the uberspect
107 * @param obj the object
108 * @param identifier the property identifier
109 * @param arg the property value
110 * @return the property setter or null
111 */
112 JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg);
113 }
114
115 /**
116 * Determines property resolution strategy.
117 *
118 * <p>To use a strategy instance, you have to set it at engine creation using
119 * {@link org.apache.commons.jexl3.JexlBuilder#strategy(JexlUberspect.ResolverStrategy)}
120 * as in:</p>
121 *
122 * {@code JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();}
123 *
124 * @since 3.0
125 */
126 interface ResolverStrategy {
127 /**
128 * Applies this strategy to a list of resolver types.
129 *
130 * @param operator the property access operator, may be null
131 * @param obj the instance we seek to obtain a property setter/getter from, cannot be null
132 * @return the ordered list of resolvers types, must not be null
133 */
134 List<PropertyResolver> apply(JexlOperator operator, Object obj);
135 }
136
137 /**
138 * A resolver types list tailored for POJOs, favors '.' over '[]'.
139 */
140 List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList(
141 JexlResolver.PROPERTY,
142 JexlResolver.MAP,
143 JexlResolver.LIST,
144 JexlResolver.DUCK,
145 JexlResolver.FIELD,
146 JexlResolver.CONTAINER
147 ));
148
149 /**
150 * A resolver types list tailored for Maps, favors '[]' over '.'.
151 */
152 List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList(
153 JexlResolver.MAP,
154 JexlResolver.LIST,
155 JexlResolver.DUCK,
156 JexlResolver.PROPERTY,
157 JexlResolver.FIELD,
158 JexlResolver.CONTAINER
159 ));
160
161 /**
162 * The default strategy.
163 * <p>
164 * If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers;
165 * Other cases use the POJO list of resolvers.
166 */
167 ResolverStrategy JEXL_STRATEGY = (op, obj) -> {
168 if (op == JexlOperator.ARRAY_GET) {
169 return MAP;
170 }
171 if (op == JexlOperator.ARRAY_SET) {
172 return MAP;
173 }
174 if (op == null && obj instanceof Map) {
175 return MAP;
176 }
177 return POJO;
178 };
179
180 /**
181 * The map strategy.
182 *
183 * <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers.
184 * Otherwise, use the POJO list of resolvers.</p>
185 */
186 ResolverStrategy MAP_STRATEGY = (op, obj) -> {
187 if (op == JexlOperator.ARRAY_GET) {
188 return MAP;
189 }
190 if (op == JexlOperator.ARRAY_SET) {
191 return MAP;
192 }
193 if (obj instanceof Map) {
194 return MAP;
195 }
196 return POJO;
197 };
198
199 /**
200 * Gets an arithmetic operator resolver for a given arithmetic instance.
201 *
202 * @param arithmetic the arithmetic instance
203 * @return the arithmetic uberspect or null if no operator method were overridden
204 * @since 3.0
205 * @see #getOperator(JexlArithmetic)
206 */
207 JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
208
209 /**
210 * Gets an arithmetic operator executor for a given arithmetic instance.
211 *
212 * @param arithmetic the arithmetic instance
213 * @return an operator uberspect instance
214 * @since 3.5.0
215 */
216 default JexlOperator.Uberspect getOperator(final JexlArithmetic arithmetic) {
217 return null;
218 }
219
220 /**
221 * Seeks a class by name using this uberspect class-loader.
222 * @param className the class name
223 * @return the class instance or null if the class cannot be located by this uberspect class loader or if
224 * permissions deny access to the class
225 */
226 default Class<?> getClassByName(final String className) {
227 try {
228 return Class.forName(className, false, getClassLoader());
229 } catch (final ClassNotFoundException xignore) {
230 return null;
231 }
232 }
233
234 /**
235 * Gets the current class loader.
236 * @return the class loader
237 */
238 ClassLoader getClassLoader();
239
240 /**
241 * Returns a class constructor.
242 *
243 * @param ctorHandle a class or class name
244 * @param args constructor arguments
245 * @return a {@link JexlMethod}
246 * @since 3.0
247 */
248 JexlMethod getConstructor(Object ctorHandle, Object... args);
249
250 /**
251 * Gets an iterator from an object.
252 *
253 * @param obj to get the iterator from
254 * @return an iterator over obj or null
255 */
256 Iterator<?> getIterator(Object obj);
257
258 /**
259 * Returns a JexlMethod.
260 *
261 * @param obj the object
262 * @param method the method name
263 * @param args method arguments
264 * @return a {@link JexlMethod}
265 */
266 JexlMethod getMethod(Object obj, String method, Object... args);
267
268 /**
269 * Property getter.
270 * <p>
271 * Seeks a JexlPropertyGet apropos to an expression like {@code bar.woogie}.</p>
272 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object)}
273 *
274 * @param resolvers the list of property resolvers to try
275 * @param obj the object to get the property from
276 * @param identifier property name
277 * @return a {@link JexlPropertyGet} or null
278 * @since 3.0
279 */
280 JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
281
282 /**
283 * Property getter.
284 *
285 * <p>returns a JelPropertySet apropos to an expression like {@code bar.woogie}.</p>
286 *
287 * @param obj the object to get the property from
288 * @param identifier property name
289 * @return a {@link JexlPropertyGet} or null
290 */
291 JexlPropertyGet getPropertyGet(Object obj, Object identifier);
292
293 /**
294 * Property setter.
295 * <p>
296 * Seeks a JelPropertySet apropos to an expression like {@code foo.bar = "geir"}.</p>
297 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object)}
298 *
299 * @param resolvers the list of property resolvers to try,
300 * @param obj the object to get the property from
301 * @param identifier property name
302 * @param arg value to set
303 * @return a {@link JexlPropertySet} or null
304 * @since 3.0
305 */
306 JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
307
308 /**
309 * Property setter.
310 * <p>
311 * Seeks a JelPropertySet apropos to an expression like {@code foo.bar = "geir"}.</p>
312 *
313 * @param obj the object to get the property from.
314 * @param identifier property name
315 * @param arg value to set
316 * @return a {@link JexlPropertySet} or null
317 */
318 JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
319
320 /**
321 * Applies this uberspect property resolver strategy.
322 *
323 * @param op the operator
324 * @param obj the object
325 * @return the applied strategy resolver list
326 */
327 List<PropertyResolver> getResolvers(JexlOperator op, Object obj);
328
329 /**
330 * Gets this uberspect version.
331 *
332 * @return the class loader modification count
333 */
334 int getVersion();
335
336 /**
337 * Sets the class loader to use.
338 *
339 * <p>This increments the version.</p>
340 *
341 * @param loader the class loader
342 */
343 void setClassLoader(ClassLoader loader);
344
345 }