    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}