    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    static final 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    static final 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    static final 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    static final 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     * Returns a class constructor.
    233     *
    234     * @param ctorHandle a class or class name
    235     * @param args       constructor arguments
    236     * @return a {@link JexlMethod}
    237     * @since 3.0
    238     */
    239    JexlMethod getConstructor(Object ctorHandle, Object... args);
    240
    241    /**
    242     * Returns a JexlMethod.
    243     *
    244     * @param obj    the object
    245     * @param method the method name
    246     * @param args   method arguments
    247     * @return a {@link JexlMethod}
    248     */
    249    JexlMethod getMethod(Object obj, String method, Object... args);
    250
    251    /**
    252     * Property getter.
    253     *
    254     * <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p>
    255     *
    256     * @param obj        the object to get the property from
    257     * @param identifier property name
    258     * @return a {@link JexlPropertyGet} or null
    259     */
    260    JexlPropertyGet getPropertyGet(Object obj, Object identifier);
    261
    262    /**
    263     * Property getter.
    264     * <p>
    265     * Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p>
    266     * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
    267     *
    268     * @param resolvers  the list of property resolvers to try
    269     * @param obj        the object to get the property from
    270     * @param identifier property name
    271     * @return a {@link JexlPropertyGet} or null
    272     * @since 3.0
    273     */
    274    JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
    275
    276    /**
    277     * Property setter.
    278     * <p>
    279     * Seeks a JelPropertySet apropos to an expression like  <code>foo.bar = "geir"</code>.</p>
    280     *
    281     * @param obj        the object to get the property from.
    282     * @param identifier property name
    283     * @param arg        value to set
    284     * @return a {@link JexlPropertySet} or null
    285     */
    286    JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
    287
    288    /**
    289     * Property setter.
    290     * <p>
    291     * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
    292     * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
    293     *
    294     * @param resolvers  the list of property resolvers to try,
    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     * @since 3.0
    300     */
    301    JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
    302
    303    /**
    304     * Gets an iterator from an object.
    305     *
    306     * @param obj to get the iterator from
    307     * @return an iterator over obj or null
    308     */
    309    Iterator<?> getIterator(Object obj);
    310
    311    /**
    312     * Gets an arithmetic operator resolver for a given arithmetic instance.
    313     *
    314     * @param arithmetic the arithmetic instance
    315     * @return the arithmetic uberspect or null if no operator method were overridden
    316     * @since 3.0
    317     */
    318    JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
    319
    320}