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