    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}