View Javadoc
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 }