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}