    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 */
    017package org.apache.commons.jexl3.internal.introspection;
    018
    019import org.apache.commons.jexl3.JexlArithmetic;
    020import org.apache.commons.jexl3.JexlOperator;
    021import org.apache.commons.jexl3.introspection.JexlMethod;
    022import org.apache.commons.jexl3.introspection.JexlPropertyGet;
    023import org.apache.commons.jexl3.introspection.JexlPropertySet;
    024import org.apache.commons.jexl3.introspection.JexlSandbox;
    025import org.apache.commons.jexl3.introspection.JexlUberspect;
    026
    027import java.util.Iterator;
    028import java.util.List;
    029
    030/**
    031 * An uberspect that controls usage of properties, methods and constructors through a sandbox.
    032 * @since 3.0
    033 */
    034public final class SandboxUberspect implements JexlUberspect {
    035    /** The base uberspect. */
    036    private final JexlUberspect uberspect;
    037    /**  The sandbox. */
    038    private final JexlSandbox sandbox;
    039
    040    /**
    041     * A constructor for JexlSandbox uberspect.
    042     * @param theUberspect the JexlUberspect to sandbox
    043     * @param theSandbox the sandbox which is copied to avoid changes at runtime
    044     */
    045    public SandboxUberspect(final JexlUberspect theUberspect, final JexlSandbox theSandbox) {
    046        if (theSandbox == null) {
    047            throw new NullPointerException("sandbox can not be null");
    048        }
    049        if (theUberspect == null) {
    050            throw new NullPointerException("uberspect can not be null");
    051        }
    052        this.uberspect = theUberspect;
    053        this.sandbox = theSandbox.copy();
    054    }
    055
    056    @Override
    057    public void setClassLoader(final ClassLoader loader) {
    058        uberspect.setClassLoader(loader);
    059    }
    060
    061    @Override
    062    public ClassLoader getClassLoader() {
    063        return uberspect.getClassLoader();
    064    }
    065
    066    @Override
    067    public int getVersion() {
    068        return uberspect.getVersion();
    069    }
    070
    071    @Override
    072    public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
    073        final String className;
    074        if (ctorHandle instanceof Class<?>) {
    075            className = sandbox.execute((Class<?>) ctorHandle, "");
    076        } else if (ctorHandle != null) {
    077            className = sandbox.execute(ctorHandle.toString(), "");
    078        } else {
    079            className = null;
    080        }
    081        return className != null && className != JexlSandbox.NULL ? uberspect.getConstructor(className, args) : null;
    082    }
    083
    084    @Override
    085    public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
    086        if (obj != null && method != null) {
    087            final Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
    088            final String actual = sandbox.execute(clazz, method);
    089            if (actual != null && actual != JexlSandbox.NULL) {
    090                return uberspect.getMethod(obj, actual, args);
    091            }
    092        }
    093        return null;
    094    }
    095
    096    @Override
    097    public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
    098        return uberspect.getResolvers(op, obj);
    099    }
    100
    101    @Override
    102    public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
    103        return getPropertyGet(null, obj, identifier);
    104    }
    105
    106    @Override
    107    public JexlPropertyGet getPropertyGet(final List<PropertyResolver> resolvers,
    108                                          final Object obj,
    109                                          final Object identifier) {
    110        if (obj != null) {
    111            if (identifier != null) {
    112                final String property = identifier.toString();
    113                final String actual = sandbox.read(obj.getClass(), property);
    114                if (actual != null) {
    115                    // no transformation, strict equality: use identifier before string conversion
    116                    final Object pty = actual == property ? identifier : actual;
    117                    return uberspect.getPropertyGet(resolvers, obj, pty);
    118                }
    119            } else {
    120                final String actual = sandbox.read(obj.getClass(), null);
    121                if (actual != JexlSandbox.NULL) {
    122                     return uberspect.getPropertyGet(resolvers, obj, null);
    123                }
    124            }
    125        }
    126        return null;
    127    }
    128
    129    @Override
    130    public JexlPropertySet getPropertySet(final Object obj,final Object identifier,final Object arg) {
    131        return getPropertySet(null, obj, identifier, arg);
    132    }
    133
    134    @Override
    135    public JexlPropertySet getPropertySet(final List<PropertyResolver> resolvers,
    136                                          final Object obj,
    137                                          final Object identifier,
    138                                          final Object arg) {
    139        if (obj != null) {
    140            if (identifier != null) {
    141                final String property = identifier.toString();
    142                final String actual = sandbox.write(obj.getClass(), property);
    143                if (actual != null) {
    144                    // no transformation, strict equality: use identifier before string conversion
    145                    final Object pty = actual == property ? identifier : actual;
    146                    return uberspect.getPropertySet(resolvers, obj, pty, arg);
    147                }
    148            } else {
    149                final String actual = sandbox.write(obj.getClass(), null);
    150                if (actual != JexlSandbox.NULL) {
    151                    return uberspect.getPropertySet(resolvers, obj, null, arg);
    152                }
    153            }
    154        }
    155        return null;
    156    }
    157
    158    @Override
    159    public Iterator<?> getIterator(final Object obj) {
    160        return uberspect.getIterator(obj);
    161    }
    162
    163    @Override
    164    public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
    165        return uberspect.getArithmetic(arithmetic);
    166    }
    167}