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 *      https://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.Collection;
022import java.util.Collections;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.commons.jexl3.JexlArithmetic;
028import org.apache.commons.jexl3.JexlOperator;
029
030/**
031 * 'Federated' introspection/reflection interface to allow JEXL introspection
032 * behavior to be customized.
033 *
034 * @since 1.0
035 */
036public interface JexlUberspect {
037    /**
038     * The various builtin property resolvers.
039     * <p>
040     * Each resolver discovers how to set/get a property with different techniques; seeking
041     * method names or field names, etc.
042     *
043     * @since 3.0
044     */
045    enum JexlResolver implements PropertyResolver {
046        /** Seeks methods named get{P,p}property and is{P,p}property. */
047        PROPERTY,
048
049        /** Seeks map methods get/put. */
050        MAP,
051
052        /** Seeks list methods to get/set. */
053        LIST,
054
055        /** Seeks any get/{set,put} method (quacking like a list or a map). */
056        DUCK,
057
058        /**  Seeks public instance members.*/
059        FIELD,
060
061        /** Seeks a getContainer(property) and setContainer(property, value) as in {@code x.container.property}. */
062        CONTAINER;
063
064        @Override
065        public final JexlPropertyGet getPropertyGet(final JexlUberspect uber,
066                                                    final Object obj,
067                                                    final Object identifier) {
068            return uber.getPropertyGet(Collections.singletonList(this), obj, identifier);
069        }
070
071        @Override
072        public final JexlPropertySet getPropertySet(final JexlUberspect uber,
073                                                    final Object obj,
074                                                    final Object identifier,
075                                                    final Object arg) {
076            return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg);
077        }
078    }
079
080    /**
081     * A marker interface that solves a simple class name into a fully qualified one.
082     * <p>The base implementation uses imports.</p>
083     * @since 3.6.0
084     */
085    interface ClassNameResolver {
086        /**
087         * Resolves a class name.
088         * @param name the simple class name
089         * @return the fully qualified class name
090         */
091        String resolveClassName(String name);
092    }
093
094    /**
095     * A marker interface that solves a class constant by name.
096     * <p>The base implementation uses imports to solve enums and public static final fields.</p>
097     * @since 3.6.0
098     */
099    interface ClassConstantResolver extends ClassNameResolver {
100        /**
101         * Resolves a constant by its name.
102         * @param name the constant name, a qualified name
103         * @return the constant value or TRY_FAILED if not found
104         */
105        Object resolveConstant(String name);
106    }
107
108    /**
109     * The factory type for creating constant resolvers.
110     * @since 3.6.0
111     */
112    interface ConstantResolverFactory {
113        /**
114         * Creates a constant resolver.
115         * @param imports the collection of imports (packages and classes) to use
116         * @return a constant resolver
117         */
118        ClassConstantResolver createConstantResolver(Collection<String> imports);
119    }
120
121    /**
122     * Abstracts getting property setter and getter.
123     * <p>
124     * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
125     * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter
126     * stops the search.
127     *
128     * @see JexlResolver
129     * @see JexlUberspect#getPropertyGet
130     * @see JexlUberspect#getPropertySet
131     * @since 3.0
132     */
133    interface PropertyResolver {
134
135        /**
136         * Gets a property getter.
137         *
138         * @param uber       the uberspect
139         * @param obj        the object
140         * @param identifier the property identifier
141         * @return the property getter or null
142         */
143        JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier);
144
145        /**
146         * Gets a property setter.
147         *
148         * @param uber       the uberspect
149         * @param obj        the object
150         * @param identifier the property identifier
151         * @param arg        the property value
152         * @return the property setter or null
153         */
154        JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg);
155    }
156
157    /**
158     * Determines property resolution strategy.
159     *
160     * <p>To use a strategy, you have to set it at engine creation using
161     * {@link org.apache.commons.jexl3.JexlBuilder#strategy(JexlUberspect.ResolverStrategy)}
162     * as in:</p>
163     *
164     * {@code JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();}
165     *
166     * @since 3.0
167     */
168    interface ResolverStrategy {
169        /**
170         * Applies this strategy to a list of resolver types.
171         *
172         * @param operator the property access operator, can be null
173         * @param obj      the instance we seek to obtain a property setter/getter from, cannot be null
174         * @return the ordered list of resolver types, cannot be null
175         */
176        List<PropertyResolver> apply(JexlOperator operator, Object obj);
177    }
178
179    /**
180     * A resolver types list tailored for POJOs, favors '.' over '[]'.
181     */
182    List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList(
183            JexlResolver.PROPERTY,
184            JexlResolver.MAP,
185            JexlResolver.LIST,
186            JexlResolver.DUCK,
187            JexlResolver.FIELD,
188            JexlResolver.CONTAINER
189    ));
190
191    /**
192     * A resolver types list tailored for Maps, favors '[]' over '.'.
193     */
194    List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList(
195            JexlResolver.MAP,
196            JexlResolver.LIST,
197            JexlResolver.DUCK,
198            JexlResolver.PROPERTY,
199            JexlResolver.FIELD,
200            JexlResolver.CONTAINER
201    ));
202
203    /**
204     * The default strategy.
205     * <p>
206     * If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers;
207     * Other cases use the POJO list of resolvers.
208     */
209    ResolverStrategy JEXL_STRATEGY = (op, obj) -> {
210        if (op == JexlOperator.ARRAY_GET) {
211            return MAP;
212        }
213        if (op == JexlOperator.ARRAY_SET) {
214            return MAP;
215        }
216        if (op == null && obj instanceof Map) {
217            return MAP;
218        }
219        return POJO;
220    };
221
222    /**
223     * The map strategy.
224     *
225     * <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers.
226     * Otherwise, use the POJO list of resolvers.</p>
227     */
228    ResolverStrategy MAP_STRATEGY = (op, obj) -> {
229        if (op == JexlOperator.ARRAY_GET) {
230            return MAP;
231        }
232        if (op == JexlOperator.ARRAY_SET) {
233            return MAP;
234        }
235        if (obj instanceof Map) {
236            return MAP;
237        }
238        return POJO;
239    };
240
241    /**
242     * Gets an arithmetic operator resolver for a given arithmetic instance.
243     *
244     * @param arithmetic the arithmetic instance
245     * @return the arithmetic uberspect or null if no operator method were overridden
246     * @since 3.0
247     * @see #getOperator(JexlArithmetic)
248     */
249    JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
250
251    /**
252     * Gets an arithmetic operator executor for a given arithmetic instance.
253     *
254     * @param arithmetic the arithmetic instance
255     * @return an operator uberspect instance
256     * @since 3.5.0
257     */
258    default JexlOperator.Uberspect getOperator(final JexlArithmetic arithmetic) {
259        return null;
260    }
261
262    /**
263     * Seeks a class by name using this uberspect class-loader.
264     * @param className the class name
265     * @return the class instance or null if the class cannot be located by this uberspect class loader or if
266     * permissions deny access to the class
267     */
268    default Class<?> getClassByName(final String className) {
269        try {
270            return Class.forName(className, false, getClassLoader());
271        } catch (final ClassNotFoundException ignore) {
272            return null;
273        }
274    }
275
276    /**
277     * Gets the current class loader.
278     * @return the class loader
279     */
280    ClassLoader getClassLoader();
281
282    /**
283     * Returns a class constructor.
284     *
285     * @param ctorHandle a class or class name
286     * @param args       constructor arguments
287     * @return a {@link JexlMethod}
288     * @since 3.0
289     */
290    JexlMethod getConstructor(Object ctorHandle, Object... args);
291
292    /**
293     * Gets an iterator from an object.
294     *
295     * @param obj to get the iterator from
296     * @return an iterator over obj or null
297     */
298    Iterator<?> getIterator(Object obj);
299
300    /**
301     * Returns a JexlMethod.
302     *
303     * @param obj    the object
304     * @param method the method name
305     * @param args   method arguments
306     * @return a {@link JexlMethod}
307     */
308    JexlMethod getMethod(Object obj, String method, Object... args);
309
310    /**
311     * Property getter.
312     * <p>
313     * Seeks a JexlPropertyGet apropos to an expression like {@code bar.woogie}.</p>
314     * See {@link ResolverStrategy#apply(JexlOperator, Object)}
315     *
316     * @param resolvers  the list of property resolvers to try
317     * @param obj        the object to get the property from
318     * @param identifier property name
319     * @return a {@link JexlPropertyGet} or null
320     * @since 3.0
321     */
322    JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
323
324    /**
325     * Property getter.
326     *
327     * <p>returns a JelPropertySet apropos to an expression like {@code bar.woogie}.</p>
328     *
329     * @param obj        the object to get the property from
330     * @param identifier property name
331     * @return a {@link JexlPropertyGet} or null
332     */
333    JexlPropertyGet getPropertyGet(Object obj, Object identifier);
334
335    /**
336     * Property setter.
337     * <p>
338     * Seeks a JelPropertySet apropos to an expression like {@code foo.bar = "geir"}.</p>
339     * See {@link ResolverStrategy#apply(JexlOperator, Object)}
340     *
341     * @param resolvers  the list of property resolvers to try,
342     * @param obj        the object to get the property from
343     * @param identifier property name
344     * @param arg        value to set
345     * @return a {@link JexlPropertySet} or null
346     * @since 3.0
347     */
348    JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
349
350    /**
351     * Property setter.
352     * <p>
353     * Seeks a JelPropertySet apropos to an expression like  {@code foo.bar = "geir"}.</p>
354     *
355     * @param obj        the object to get the property from.
356     * @param identifier property name
357     * @param arg        value to set
358     * @return a {@link JexlPropertySet} or null
359     */
360    JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
361
362    /**
363     * Applies this uberspect property resolver strategy.
364     *
365     * @param op the operator
366     * @param obj the object
367     * @return the applied strategy resolver list
368     */
369    List<PropertyResolver> getResolvers(JexlOperator op, Object obj);
370
371    /**
372     * Gets this uberspect version.
373     *
374     * @return the class loader modification count
375     */
376    int getVersion();
377
378    /**
379     * Sets the class loader to use.
380     *
381     * <p>This increments the version.</p>
382     *
383     * @param loader the class loader
384     */
385    void setClassLoader(ClassLoader loader);
386
387}