001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jexl3.introspection;
019
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.jexl3.JexlArithmetic;
027import org.apache.commons.jexl3.JexlOperator;
028
029/**
030 * 'Federated' introspection/reflection interface to allow JEXL introspection
031 * behavior to be customized.
032 *
033 * @since 1.0
034 */
035public interface JexlUberspect {
036    /**
037     * The various builtin property resolvers.
038     * <p>
039     * Each resolver discovers how to set/get a property with different techniques; seeking
040     * method names or field names, etc.
041     *
042     * @since 3.0
043     */
044    enum JexlResolver implements PropertyResolver {
045        /** Seeks methods named get{P,p}property and is{P,p}property. */
046        PROPERTY,
047
048        /** Seeks map methods get/put. */
049        MAP,
050
051        /** Seeks list methods get/set. */
052        LIST,
053
054        /** Seeks any get/{set,put} method (quacking like a list or a map). */
055        DUCK,
056
057        /**  Seeks public instance members.*/
058        FIELD,
059
060        /** Seeks a getContainer(property) and setContainer(property, value) as in {@code x.container.property}. */
061        CONTAINER;
062
063        @Override
064        public final JexlPropertyGet getPropertyGet(final JexlUberspect uber,
065                                                    final Object obj,
066                                                    final Object identifier) {
067            return uber.getPropertyGet(Collections.singletonList(this), obj, identifier);
068        }
069
070        @Override
071        public final JexlPropertySet getPropertySet(final JexlUberspect uber,
072                                                    final Object obj,
073                                                    final Object identifier,
074                                                    final Object arg) {
075            return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg);
076        }
077    }
078
079    /**
080     * Abstracts getting property setter and getter.
081     * <p>
082     * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
083     * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter
084     * stops the search.
085     *
086     * @see JexlResolver
087     * @see JexlUberspect#getPropertyGet
088     * @see JexlUberspect#getPropertySet
089     * @since 3.0
090     */
091    interface PropertyResolver {
092
093        /**
094         * Gets a property getter.
095         *
096         * @param uber       the uberspect
097         * @param obj        the object
098         * @param identifier the property identifier
099         * @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}