    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 org.apache.commons.jexl3.JexlArithmetic;
    021import org.apache.commons.jexl3.JexlOperator;
    022
    023import java.util.Arrays;
    024import java.util.Collections;
    025import java.util.Iterator;
    026import java.util.List;
    027import java.util.Map;
    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     * Abstracts getting property setter and getter.
    038     * <p>
    039     * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
    040     * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter
    041     * stops the search.
    042     *
    043     * @see JexlResolver
    044     * @see JexlUberspect#getPropertyGet
    045     * @see JexlUberspect#getPropertySet
    046     * @since 3.0
    047     */
    048    interface PropertyResolver {
    049
    050        /**
    051         * Gets a property getter.
    052         *
    053         * @param uber       the uberspect
    054         * @param obj        the object
    055         * @param identifier the property identifier
    056         * @return the property getter or null
    057         */
    058        JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier);
    059
    060        /**
    061         * Gets a property setter.
    062         *
    063         * @param uber       the uberspect
    064         * @param obj        the object
    065         * @param identifier the property identifier
    066         * @param arg        the property value
    067         * @return the property setter or null
    068         */
    069        JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg);
    070    }
    071
    072    /**
    073     * The various builtin property resolvers.
    074     * <p>
    075     * Each resolver discovers how to set/get a property with different techniques; seeking
    076     * method names or field names, etc.
    077     *
    078     * @since 3.0
    079     */
    080    enum JexlResolver implements PropertyResolver {
    081        /** Seeks methods named get{P,p}property and is{P,p}property. */
    082        PROPERTY,
    083
    084        /** Seeks map methods get/put. */
    085        MAP,
    086
    087        /** Seeks list methods get/set. */
    088        LIST,
    089
    090        /** Seeks any get/{set,put} method (quacking like a list or a map). */
    091        DUCK,
    092
    093        /**  Seeks public instance members.*/
    094        FIELD,
    095
    096        /** Seeks a getContainer(property) and setContainer(property, value) as in <code>x.container.property</code>. */
    097        CONTAINER;
    098
    099        @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}