    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    package org.apache.commons.jexl2.introspection;
    018
    019    import org.apache.commons.jexl2.internal.Introspector;
    020    import java.lang.reflect.Constructor;
    021    import java.lang.reflect.Field;
    022    import java.lang.reflect.Modifier;
    023    import java.util.Enumeration;
    024    import java.util.Iterator;
    025
    026    import java.util.Map;
    027    import org.apache.commons.jexl2.JexlInfo;
    028    import org.apache.commons.jexl2.JexlException;
    029    import org.apache.commons.jexl2.internal.AbstractExecutor;
    030    import org.apache.commons.jexl2.internal.ArrayIterator;
    031    import org.apache.commons.jexl2.internal.EnumerationIterator;
    032    import org.apache.commons.jexl2.internal.introspection.MethodKey;
    033    import org.apache.commons.logging.Log;
    034
    035    /**
    036     * Implementation of Uberspect to provide the default introspective
    037     * functionality of JEXL.
    038     * <p>This is the class to derive to customize introspection.</p>
    039     *
    040     * @since 1.0
    041     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
    042     * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
    043     * @version $Id: UberspectImpl.java 896952 2010-01-07 18:21:29Z henrib $
    044     */
    045    public class UberspectImpl extends Introspector implements Uberspect {
    046        /**
    047         * Publicly exposed special failure object returned by tryInvoke.
    048         */
    049        public static final Object TRY_FAILED = AbstractExecutor.TRY_FAILED;
    050
    051        /**
    052         * Creates a new UberspectImpl.
    053         * @param runtimeLogger the logger used for all logging needs
    054         */
    055        public UberspectImpl(Log runtimeLogger) {
    056            super(runtimeLogger);
    057        }
    058
    059        /**
    060         * {@inheritDoc}
    061         */
    062        @SuppressWarnings("unchecked")
    063        public Iterator<?> getIterator(Object obj, JexlInfo info) {
    064            if (obj instanceof Iterator<?>) {
    065                return ((Iterator<?>) obj);
    066            }
    067            if (obj.getClass().isArray()) {
    068                return new ArrayIterator(obj);
    069            }
    070            if (obj instanceof Map<?,?>) {
    071                return ((Map<?,?>) obj).values().iterator();
    072            }
    073            if (obj instanceof Enumeration<?>) {
    074                return new EnumerationIterator<Object>((Enumeration<Object>) obj);
    075            }
    076            if (obj instanceof Iterable<?>) {
    077                return ((Iterable<?>) obj).iterator();
    078            }
    079            try {
    080                // look for an iterator() method to support the JDK5 Iterable
    081                // interface or any user tools/DTOs that want to work in
    082                // foreach without implementing the Collection interface
    083                AbstractExecutor.Method it = getMethodExecutor(obj, "iterator", null);
    084                if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
    085                    return (Iterator<Object>) it.execute(obj, null);
    086                }
    087            } catch(Exception xany) {
    088                throw new JexlException(info, "unable to generate iterator()", xany);
    089            }
    090            return null;
    091        }
    092
    093        /**
    094         * Returns a class field.
    095         * @param obj the object
    096         * @param name the field name
    097         * @param info debug info
    098         * @return a {@link Field}.
    099         */
    100        public Field getField(Object obj, String name, JexlInfo info) {
    101            final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
    102            return getField(clazz, name);
    103        }
    104
    105        /**
    106         * {@inheritDoc}
    107         */
    108        public Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo info) {
    109            return getConstructor(ctorHandle, args);
    110        }
    111
    112        /**
    113         * {@inheritDoc}
    114         */
    115        public JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info) {
    116            return getMethodExecutor(obj, method, args);
    117        }
    118
    119        /**
    120         * A JexlPropertyGet for public fields.
    121         */
    122        public static final class FieldPropertyGet implements JexlPropertyGet {
    123            /**
    124             * The public field.
    125             */
    126            private final Field field;
    127
    128            /**
    129             * Creates a new instance of FieldPropertyGet.
    130             * @param theField the class public field
    131             */
    132            public FieldPropertyGet(Field theField) {
    133                field = theField;
    134            }
    135
    136            /**
    137             * {@inheritDoc}
    138             */
    139            public Object invoke(Object obj) throws Exception {
    140                return field.get(obj);
    141            }
    142
    143            /**
    144             * {@inheritDoc}
    145             */
    146            public Object tryInvoke(Object obj, Object key) {
    147                if (obj.getClass().equals(field.getDeclaringClass()) && key.equals(field.getName())) {
    148                    try {
    149                        return field.get(obj);
    150                    } catch (IllegalAccessException xill) {
    151                        return TRY_FAILED;
    152                    }
    153                }
    154                return TRY_FAILED;
    155            }
    156
    157            /**
    158             * {@inheritDoc}
    159             */
    160            public boolean tryFailed(Object rval) {
    161                return rval == TRY_FAILED;
    162            }
    163
    164            /**
    165             * {@inheritDoc}
    166             */
    167            public boolean isCacheable() {
    168                return true;
    169            }
    170        }
    171
    172        /**
    173         * {@inheritDoc}
    174         */
    175        public JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info) {
    176            JexlPropertyGet get = getGetExecutor(obj, identifier);
    177            if (get == null && obj != null && identifier != null) {
    178                Field field = getField(obj, identifier.toString(), info);
    179                if (field != null) {
    180                    return new FieldPropertyGet(field);
    181                }
    182            }
    183            return get;
    184        }
    185
    186        /**
    187         * A JexlPropertySet for public fields.
    188         */
    189        public static final class FieldPropertySet implements JexlPropertySet {
    190            /**
    191             * The public field.
    192             */
    193            private final Field field;
    194
    195            /**
    196             * Creates a new instance of FieldPropertySet.
    197             * @param theField the class public field
    198             */
    199            public FieldPropertySet(Field theField) {
    200                field = theField;
    201            }
    202
    203            /**
    204             * {@inheritDoc}
    205             */
    206            public Object invoke(Object obj, Object arg) throws Exception {
    207                field.set(obj, arg);
    208                return arg;
    209            }
    210
    211            /**
    212             * {@inheritDoc}
    213             */
    214            public Object tryInvoke(Object obj, Object key, Object value) {
    215                if (obj.getClass().equals(field.getDeclaringClass())
    216                    && key.equals(field.getName())
    217                    && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
    218                    try {
    219                        field.set(obj, value);
    220                        return value;
    221                    } catch (IllegalAccessException xill) {
    222                        return TRY_FAILED;
    223                    }
    224                }
    225                return TRY_FAILED;
    226            }
    227
    228            /**
    229             * {@inheritDoc}
    230             */
    231            public boolean tryFailed(Object rval) {
    232                return rval == TRY_FAILED;
    233            }
    234
    235            /**
    236             * {@inheritDoc}
    237             */
    238            public boolean isCacheable() {
    239                return true;
    240            }
    241        }
    242
    243        /**
    244         * {@inheritDoc}
    245         */
    246        public JexlPropertySet getPropertySet(final Object obj, final Object identifier, Object arg, JexlInfo info) {
    247            JexlPropertySet set = getSetExecutor(obj, identifier, arg);
    248            if (set == null && obj != null && identifier != null) {
    249                Field field = getField(obj, identifier.toString(), info);
    250                if (field != null
    251                    && !Modifier.isFinal(field.getModifiers())
    252                    && (arg == null || MethodKey.isInvocationConvertible(field.getType(), arg.getClass(), false))) {
    253                    return new FieldPropertySet(field);
    254                }
    255            }
    256            return set;
    257        }
    258    }