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</code>. */
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();</code>
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, can not 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      */
206     JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
207 
208     /**
209      * Seeks a class by name using this uberspect class-loader.
210      * @param className the class name
211      * @return the class instance or null if the class cannot be located by this uberspect class loader or if
212      * permissions deny access to the class
213      */
214     default Class<?> getClassByName(final String className) {
215         try {
216             return Class.forName(className, false, getClassLoader());
217         } catch (final ClassNotFoundException xignore) {
218             return null;
219         }
220     }
221 
222     /**
223      * Gets the current class loader.
224      * @return the class loader
225      */
226     ClassLoader getClassLoader();
227 
228     /**
229      * Returns a class constructor.
230      *
231      * @param ctorHandle a class or class name
232      * @param args       constructor arguments
233      * @return a {@link JexlMethod}
234      * @since 3.0
235      */
236     JexlMethod getConstructor(Object ctorHandle, Object... args);
237 
238     /**
239      * Gets an iterator from an object.
240      *
241      * @param obj to get the iterator from
242      * @return an iterator over obj or null
243      */
244     Iterator<?> getIterator(Object obj);
245 
246     /**
247      * Returns a JexlMethod.
248      *
249      * @param obj    the object
250      * @param method the method name
251      * @param args   method arguments
252      * @return a {@link JexlMethod}
253      */
254     JexlMethod getMethod(Object obj, String method, Object... args);
255 
256     /**
257      * Property getter.
258      * <p>
259      * Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p>
260      * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
261      *
262      * @param resolvers  the list of property resolvers to try
263      * @param obj        the object to get the property from
264      * @param identifier property name
265      * @return a {@link JexlPropertyGet} or null
266      * @since 3.0
267      */
268     JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
269 
270     /**
271      * Property getter.
272      *
273      * <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p>
274      *
275      * @param obj        the object to get the property from
276      * @param identifier property name
277      * @return a {@link JexlPropertyGet} or null
278      */
279     JexlPropertyGet getPropertyGet(Object obj, Object identifier);
280 
281     /**
282      * Property setter.
283      * <p>
284      * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
285      * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
286      *
287      * @param resolvers  the list of property resolvers to try,
288      * @param obj        the object to get the property from
289      * @param identifier property name
290      * @param arg        value to set
291      * @return a {@link JexlPropertySet} or null
292      * @since 3.0
293      */
294     JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
295 
296     /**
297      * Property setter.
298      * <p>
299      * Seeks a JelPropertySet apropos to an expression like  <code>foo.bar = "geir"</code>.</p>
300      *
301      * @param obj        the object to get the property from.
302      * @param identifier property name
303      * @param arg        value to set
304      * @return a {@link JexlPropertySet} or null
305      */
306     JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
307 
308     /**
309      * Applies this uberspect property resolver strategy.
310      *
311      * @param op the operator
312      * @param obj the object
313      * @return the applied strategy resolver list
314      */
315     List<PropertyResolver> getResolvers(JexlOperator op, Object obj);
316 
317     /**
318      * Gets this uberspect version.
319      *
320      * @return the class loader modification count
321      */
322     int getVersion();
323 
324     /**
325      * Sets the class loader to use.
326      *
327      * <p>This increments the version.</p>
328      *
329      * @param loader the class loader
330      */
331     void setClassLoader(ClassLoader loader);
332 
333 }