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