zk/src/main/java/org/zkoss/zk/ui/Components.java

Summary

Maintainability
F
1 wk
Test Coverage
/* Components.java

    Purpose:
        
    Description:
        
    History:
        Mon Jun 13 20:55:18     2005, Created by tomyeh

Copyright (C) 2005 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
    This program is distributed under LGPL Version 2.1 in the hope that
    it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zk.ui;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.security.Principal;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.zkoss.idom.Document;
import org.zkoss.lang.Objects;
import org.zkoss.util.CollectionsX;
import org.zkoss.xel.ExpressionFactory;
import org.zkoss.xel.VariableResolver;
import org.zkoss.zk.au.AuResponse;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.ext.Scope;
import org.zkoss.zk.ui.ext.ScopeListener;
import org.zkoss.zk.ui.metainfo.ComponentDefinition;
import org.zkoss.zk.ui.metainfo.LanguageDefinition;
import org.zkoss.zk.ui.metainfo.PageDefinition;
import org.zkoss.zk.ui.sys.ExecutionCtrl;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.xel.Evaluator;

/**
 * Utilities to access {@link Component}.
 *
 * @author tomyeh
 */
public class Components {
    private static final Logger _zklog = LoggerFactory.getLogger("org.zkoss.zk.log");

    protected Components() {
    }

    /** Returns the parent of the ID space, or null if not found.
     * @since 5.0.0
     */
    public static IdSpace getParentIdSpace(IdSpace idspace) {
        if (idspace instanceof Component) {
            final Component c = (Component) idspace;
            final Component p = c.getParent();
            return p != null ? p.getSpaceOwner() : c.getPage();
        }
        return null;
    }

    /** Sorts the components in the list.
     *
     * <p>Note: you cannot use {@link Collections#sort} to sort
     * {@link Component#getChildren} because Collections.sort might cause
     * some replicated item in the list.
     * @see #sort(List, int, int, Comparator)
     */
    public static void sort(List<? extends Component> list, Comparator<? super Component> cpr) {
        sort(list, 0, list.size(), cpr);
    }

    /**
     * Sorts the components in the list.
     * @param list the list to be sorted
     * @param from the index of the first element (inclusive) to be sorted
     * @param to the index of the last element (exclusive) to be sorted
     * @param cpr the comparator to determine the order of the list.
     * @since 3.5.0
     */
    public static void sort(List<? extends Component> list, int from, int to, Comparator<? super Component> cpr) {
        final Component[] ary = CollectionsX.toArray(list, new Component[0], from, to);
        Arrays.sort(ary, cpr);

        ListIterator<? extends Component> it = list.listIterator(from);
        int j = 0, k = to - from;
        for (; it.hasNext() && --k >= 0; ++j) {
            if (it.next() != ary[j]) {
                it.remove();

                if (it.hasNext() && --k >= 0) {
                    if (it.next() == ary[j])
                        continue;
                    it.previous();
                    ++k;
                }
                break;
            }
        }
        while (it.hasNext() && --k >= 0) {
            it.next();
            it.remove();
        }
        for (; j < ary.length; ++j)
            add(list, from + j, ary[j]);
    }

    /** Replaces a component with another.
     * @param oldc the component to remove.
     * @param newc the component to add
     * @exception IllegalArgumentException if oldc's parent and page are
     * both null.
     * @since 3.5.2
     */
    public static void replace(Component oldc, Component newc) {
        final Component p = oldc.getParent(), sib = oldc.getNextSibling();
        if (p != null) {
            oldc.detach();
            p.insertBefore(newc, sib);
        } else {
            final Page page = oldc.getPage();
            if (page == null)
                throw new IllegalArgumentException("Neither child nor attached, " + oldc);
            oldc.detach();
            if (newc.getParent() != null)
                newc.detach();
            newc.setPageBefore(page, sib);
        }
    }

    /** Replaces all children of the specified component.
     * It is the same as
     * <pre><code>parent.getChildren().clear();
     *parent.getChildren().addAll(newChildren);
     *</code></pre>
     * @since 3.5.2
     */
    public static void replaceChildren(Component parent, Collection<Component> newChildren) {
        final Collection<Component> children = parent.getChildren();
        children.clear();
        children.addAll(newChildren);
    }

    @SuppressWarnings("unchecked")
    private static void add(List list, int index, Object o) { //to minimize the unchecked range
        list.add(index, o);
    }

    /** Returns the root component of the specified one.
     * Notice that it could return <code>comp</code>, if it is already
     * a root component (or it is null).
     * @since 3.6.3
     */
    public static Component getRoot(Component comp) {
        if (comp == null)
            return null;
        for (;;) {
            final Component p = comp.getParent();
            if (p == null)
                return comp;
            comp = p;
        }
    }

    /** Tests whether node1 is an ancestor of node 2.
     * If node1 and node2 is the same, true is returned.
     */
    public static boolean isAncestor(Component node1, Component node2) {
        for (; node2 != null; node2 = node2.getParent()) {
            if (node1 == node2)
                return true;
        }
        return false;
    }

    /** Removes all children of the specified component.
     * It is the same as <code>comp.getChildren().clear()</code>.
     */
    public static void removeAllChildren(Component comp) {
        comp.getChildren().clear();
    }

    /** Returns the component definition of the specified class in all
     * language of the specified device, or null if not found
     *
     * @param deviceType the device type ({@link org.zkoss.zk.device.Device}),
     * such as ajax. It cannot be null.
     * @param cls the implementation class of the component.
     * @since 5.0.0
     */
    public static final ComponentDefinition getDefinitionByDeviceType(String deviceType, Class cls) {
        for (LanguageDefinition ld : LanguageDefinition.getByDeviceType(deviceType)) {
            ComponentDefinition definitionIfAny = ld.getComponentDefinitionIfAny(
                    cls);
            if (definitionIfAny != null) {
                return definitionIfAny;
            }
        }
        return null;
    }

    /** Returns whether this component is real visible (all its parents
     * are visible).
     * <p>Note: true is returned if comp is null.
     * In other words, it can be used to examine parent's real visibility
     * even if it is a root component,
     * such as <code>Components.isRealVisible(getParent())</code>.
     * @see Component#isVisible
     */
    public static boolean isRealVisible(Component comp) {
        for (; comp != null; comp = comp.getParent())
            if (!comp.isVisible())
                return false;
        return true;
    }

    /** Returns a collection of visible children.
     * <p>The performance of the returned collection's size() is NO GOOD.
     */
    public static Collection<Component> getVisibleChildren(Component comp) {
        final Collection<Component> children = comp.getChildren();
        return new AbstractCollection<Component>() {
            public int size() {
                int size = 0;
                for (Component c : children) {
                    if (c.isVisible())
                        ++size;
                }
                return size;
            }

            public Iterator<Component> iterator() {
                return new Iterator<Component>() {
                    final Iterator<Component> _it = children.iterator();
                    Component _next;

                    public boolean hasNext() {
                        if (_next != null)
                            return true;
                        _next = getNextVisible(false);
                        return _next != null;
                    }

                    public Component next() {
                        if (_next != null) {
                            final Component c = _next;
                            _next = null;
                            return c;
                        }
                        return getNextVisible(true);
                    }

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }

                    private Component getNextVisible(boolean blind) {
                        while (blind || _it.hasNext()) {
                            final Component c = _it.next();
                            if (c.isVisible())
                                return c;
                        }
                        return null;
                    }
                };
            }
        };
    }

    /** Converts a string to an integer that can be used to access
     * {@link Component#getAttribute(String, int)}
     */
    public static final int getScope(String scope) {
        if ("component".equals(scope))
            return Component.COMPONENT_SCOPE;
        if ("space".equals(scope))
            return Component.SPACE_SCOPE;
        if ("page".equals(scope))
            return Component.PAGE_SCOPE;
        if ("desktop".equals(scope))
            return Component.DESKTOP_SCOPE;
        if ("session".equals(scope))
            return Component.SESSION_SCOPE;
        if ("application".equals(scope))
            return Component.APPLICATION_SCOPE;
        if ("request".equals(scope))
            return Component.REQUEST_SCOPE;
        throw new IllegalArgumentException("Unknown scope: " + scope);
    }

    /** Converts an integer to the string representing the scope.
     * @param scope one of {@link Component#COMPONENT_SCOPE},
     * {@link Component#SPACE_SCOPE}, {@link Component#PAGE_SCOPE}, 
     * {@link Component#DESKTOP_SCOPE}, {@link Component#SESSION_SCOPE},
     * {@link Component#REQUEST_SCOPE}, and {@link Component#APPLICATION_SCOPE}.
     */
    public static final String scopeToString(int scope) {
        switch (scope) {
        case Component.COMPONENT_SCOPE:
            return "component";
        case Component.SPACE_SCOPE:
            return "space";
        case Component.PAGE_SCOPE:
            return "page";
        case Component.DESKTOP_SCOPE:
            return "desktop";
        case Component.SESSION_SCOPE:
            return "session";
        case Component.APPLICATION_SCOPE:
            return "application";
        case Component.REQUEST_SCOPE:
            return "request";
        }
        throw new IllegalArgumentException("Unknown scope: " + scope);
    }

    /** Converts a component to a path (relevant to another component).
     * It is usefully to implement a serializable component that contains
     * a reference to another component. In this case, we can not
     * serializes the reference directly (otherwise, another component will
     * be created, when deserialized).
     *
     * <p>Rather, it is better to store the path related, and then restore
     * it back to a component by calling {@link #pathToComponent}.
     *
     * @param comp the component to be converted to path. It cannot be null.
     * @param ref the component used to generated the path from.
     * It cannot be null.
     * @return the path. Notice that you have to use {@link #pathToComponent}
     * to convert it back.
     * @exception UnsupportedOperationException if we cannot find a path
     * to the component to write.
     * @since 3.0.0
     */
    public static final String componentToPath(Component comp, Component ref) {
        //Implementation Note:
        //The path being written is a bit different to Path, if ref
        //is not an space owner
        //For example, if comp is the space owner, "" is written.
        //If comp is the same as ref, "." is written.
        if (comp == null) {
            return null;
        } else if (comp == ref) {
            return ".";
        } else {
            final String id = comp.getId();
            if (!(comp instanceof IdSpace) && id.length() == 0)
                throw new UnsupportedOperationException("comp must be assigned with ID or a space owner: " + comp);

            final StringBuffer sb = new StringBuffer(128);
            for (IdSpace space = ref.getSpaceOwner();;) {
                if (comp == space) {
                    return sb.toString(); //could be ""
                    //we don't generate id to make it work even if
                    //its ID is changed
                } else if (space.getFellowIfAny(id) == comp) {
                    if (sb.length() > 0)
                        sb.append('/');
                    return sb.append(id).toString();
                }

                if (sb.length() > 0)
                    sb.append('/');
                sb.append("..");

                final Component parent = space instanceof Component ? ((Component) space).getParent() : null;
                if (parent == null)
                    throw new UnsupportedOperationException("Unable to locate " + comp + " from " + ref);
                space = parent.getSpaceOwner();
            }
        }
    }

    /** Converts a path, generated by {@link #componentToPath}, to
     * a component.
     *
     * @param ref the component used to generated the path from.
     * It cannot be null. It is the same as the one when calling
     * {@link #componentToPath}.
     * @since 3.0.0
     */
    public static final Component pathToComponent(String path, Component ref) {
        if (path == null) {
            return null;
        } else if (".".equals(path)) {
            return ref;
        } else if ("".equals(path)) {
            final IdSpace owner = ref.getSpaceOwner();
            if (!(owner instanceof Component))
                throw new IllegalStateException("The component is moved after serialized: " + ref);
            return (Component) owner;
        }
        return Path.getComponent(ref.getSpaceOwner(), path);
    }

    /**
     * Returns whether the given id is an implicit ZK object id.
     * 
     * @param id Component id
     * @return whether the given name is a implicit object.
     * @since 3.5.2
     */
    public static boolean isImplicit(String id) {
        return IMPLICIT_NAMES.contains(id);
    }

    /** Returns a readonly collection of the names of the implicit objects.
     * @since 6.0.0
     */
    public static Collection<String> getImplicitNames() {
        return IMPLICIT_NAMES;
    }

    private static final Set<String> IMPLICIT_NAMES = new HashSet<String>();

    static {
        final String[] names = { "application", "applicationScope", "arg", "componentScope", "desktop", "desktopScope",
                "execution", "event", //since 3.6.1, #bug 2681819: normal page throws exception after installed zkspring
                "self", "session", "sessionScope", "spaceOwner", "spaceScope", "page", "pageScope", "requestScope",
                "param" };
        for (int j = 0; j < names.length; ++j)
            IMPLICIT_NAMES.add(names[j]);
    }

    /** Returns the implicit object of the specified name, or null
     * if not found.
     *
     * <p>Notice that it does check for the current scope
     * ({@link org.zkoss.zk.ui.ext.Scopes#getCurrent}).
     * Rather, {@link org.zkoss.zk.ui.ext.Scopes#getImplicit}
     * depends on this method.
     *
     * @param page the page. If page is null and comp is not,
     * comp.getPage() is assumed
     * @see org.zkoss.zk.ui.ext.Scopes#getImplicit
     * @since 5.0.0
     */
    public static Object getImplicit(Page page, Component comp, String name) {
        if (comp != null && page == null)
            page = getCurrentPage(comp);

        if ("log".equals(name))
            return _zklog;
        if ("self".equals(name))
            return comp != null ? comp : page;
        if ("spaceOwner".equals(name))
            return comp != null ? comp.getSpaceOwner() : page;
        if ("page".equals(name))
            return page;
        if ("desktop".equals(name))
            return comp != null ? getDesktop(comp) : page.getDesktop();
        if ("session".equals(name))
            return comp != null ? getSession(comp) : page.getDesktop().getSession();
        if ("application".equals(name))
            return comp != null ? getWebApp(comp) : page.getDesktop().getWebApp();
        if ("componentScope".equals(name))
            return comp != null ? comp.getAttributes() : Collections.EMPTY_MAP;
        if ("spaceScope".equals(name)) {
            final Scope scope = comp != null ? comp.getSpaceOwner() : page;
            return scope != null ? scope.getAttributes() : Collections.EMPTY_MAP;
        }
        if ("pageScope".equals(name))
            return page != null ? page.getAttributes() : Collections.EMPTY_MAP;
        if ("desktopScope".equals(name)) {
            final Desktop dt = comp != null ? getDesktop(comp) : page.getDesktop();
            return dt != null ? dt.getAttributes() : Collections.EMPTY_MAP;
        }
        if ("sessionScope".equals(name)) {
            final Session sess = comp != null ? getSession(comp) : page.getDesktop().getSession();
            return sess != null ? sess.getAttributes() : Collections.EMPTY_MAP;
        }
        if ("applicationScope".equals(name)) {
            final WebApp app = comp != null ? getWebApp(comp) : page.getDesktop().getWebApp();
            return app != null ? app.getAttributes() : Collections.EMPTY_MAP;
        }
        if ("executionScope".equals(name)) {
            final Execution exec = Executions.getCurrent();
            return exec != null ? exec.getAttributes() : Collections.EMPTY_MAP;
        }
        if ("requestScope".equals(name))
            return REQUEST_SCOPE_PROXY;
        if ("execution".equals(name))
            return EXECUTION_PROXY;
        if ("arg".equals(name)) {
            final Execution exec = Executions.getCurrent();
            return exec != null ? exec.getArg() : null;
            //bug 2937096: composer.arg shall be statically wired 
            //arg is a Map prepared by application developer, so can be wired statically 
        }
        if ("param".equals(name)) {
            final Execution exec = Executions.getCurrent();
            return exec != null ? exec.getParameterMap() : null;
            //bug 2945974: composer.param shall be statically wired
            //Note that request parameter is prepared by servlet container, you shall not
            //copy the reference to this map; rather, you shall clone the key-value pair one-by-one.
        }
        //20090314, Henri Chen: No way to support "event" with an event proxy because org.zkoss.zk.Event is not an interface
        return null;
    }

    /** Returns the implicit object of the specified name, or null
     * if not found.
     * <p>It is the same as getImplicit(null, comp, name).
     * @since 3.6.0
     */
    public static Object getImplicit(Component comp, String name) {
        return getImplicit(null, comp, name);
    }

    /** Returns the implicit object of the specified name, or null
     * if not found.
     * <p>It is the same as getImplicit(page, null, name).
     * @since 3.6.0
     */
    public static Object getImplicit(Page page, String name) {
        return getImplicit(page, null, name);
    }

    /** Returns the composer object, or null
     * if not found.
     * @since 7.0.3
     */
    public static Composer getComposer(Component comp) {
        if (comp != null) {
            Object onm = comp.getAttribute("composerName");
            if (onm instanceof String && ((String) onm).length() > 0) {
                return (Composer) comp.getAttribute((String) onm);
            } else {
                Composer result = (Composer) comp.getAttribute("_$composer$_");
                if (result == null) {
                    // just in case
                    result = (Composer) comp.getAttribute("$composer");
                }
                return result;
            }
        }
        return null;
    }

    /**
     * Adds the smartUpdate command to the specific component.
     * @since 7.0.3
     */
    public static void smartUpdate(Component comp, String key, Object value, boolean append) {
        if (comp instanceof AbstractComponent)
            ((AbstractComponent) comp).smartUpdate(key, value, append);
    }

    private static Desktop getDesktop(Component comp) {
        final Desktop dt = comp.getDesktop();
        if (dt != null)
            return dt;
        final Execution exec = Executions.getCurrent();
        return exec != null ? exec.getDesktop() : null;
    }

    private static WebApp getWebApp(Component comp) {
        final Desktop dt = getDesktop(comp);
        return dt != null ? dt.getWebApp() : null;
    }

    private static Session getSession(Component comp) {
        final Desktop dt = getDesktop(comp);
        return dt != null ? dt.getSession() : null;
    }

    /** Returns the page of the give component, or the current page if the
     * component is null or it doesn't belong to any page.
     * The current page is retrieved by {@link ExecutionCtrl#getCurrentPage}
     * or the current execution. This method returns null if no execution
     * or no current page at all.
     * @param comp the component to retrieve the page. Ignored if null.
     * @since 6.0.0
     */
    public static Page getCurrentPage(Component comp) {
        if (comp != null) {
            Page page = comp.getPage();
            if (page != null)
                return page;
        }

        final Execution exec = Executions.getCurrent();
        return exec != null ? ((ExecutionCtrl) exec).getCurrentPage() : null;
    }

    /** Execution Proxy */
    public static final Exec EXECUTION_PROXY = new Exec();

    /** Request Scope Proxy */
    public static final RequestScope REQUEST_SCOPE_PROXY = new RequestScope();

    //Proxy to read current execution
    private static class Exec implements Execution {
        private static final Execution exec() {
            return Executions.getCurrent();
        }

        public void addAuResponse(AuResponse response) {
            exec().addAuResponse(response);
        }

        public void addAuResponse(String key, AuResponse response) {
            exec().addAuResponse(key, response);
        }

        public Component createComponents(PageDefinition pagedef, Component parent, Map<?, ?> arg) {
            return exec().createComponents(pagedef, parent, arg);
        }

        public Component createComponents(String uri, Component parent, Map<?, ?> arg) {
            return exec().createComponents(uri, parent, arg);
        }

        public Component createComponents(PageDefinition pagedef, Component parent, Component insertBefore,
                VariableResolver resolver) {
            return exec().createComponents(pagedef, parent, insertBefore, resolver);
        }

        public Component createComponents(String uri, Component parent, Component insertBefore,
                VariableResolver resolver) {
            return exec().createComponents(uri, parent, insertBefore, resolver);
        }

        public Component[] createComponents(PageDefinition pagedef, Map<?, ?> arg) {
            return exec().createComponents(pagedef, arg);
        }

        public Component[] createComponents(String uri, Map<?, ?> arg) {
            return exec().createComponents(uri, arg);
        }

        public Component[] createComponents(String uri, Page page, VariableResolver resolver, Map<?, ?> arg) {
            return exec().createComponents(uri, page, resolver, arg);
        }

        public Component[] createComponents(String uri, Component parent, Component insertBefore,
                VariableResolver resolver, Map<?, ?> arg) {
            return exec().createComponents(uri, parent, insertBefore, resolver, arg);
        }

        public Component createComponentsDirectly(String content, String extension, Component parent, Map<?, ?> arg) {
            return exec().createComponentsDirectly(content, extension, parent, arg);
        }

        public Component createComponentsDirectly(Document content, String extension, Component parent, Map<?, ?> arg) {
            return exec().createComponentsDirectly(content, extension, parent, arg);
        }

        public Component createComponentsDirectly(Reader reader, String extension, Component parent, Map<?, ?> arg)
                throws IOException {
            return exec().createComponentsDirectly(reader, extension, parent, arg);
        }

        public Component createComponentsDirectly(String content, String extension, Component parent,
                Component insertBefore, VariableResolver resolver) {
            return exec().createComponentsDirectly(content, extension, parent, insertBefore, resolver);
        }

        public Component createComponentsDirectly(Document content, String extension, Component parent,
                Component insertBefore, VariableResolver resolver) {
            return exec().createComponentsDirectly(content, extension, parent, insertBefore, resolver);
        }

        public Component createComponentsDirectly(Reader reader, String extension, Component parent,
                Component insertBefore, VariableResolver resolver) throws IOException {
            return exec().createComponentsDirectly(reader, extension, parent, insertBefore, resolver);
        }

        public Component[] createComponentsDirectly(String content, String extension, Map<?, ?> arg) {
            return exec().createComponentsDirectly(content, extension, arg);
        }

        public Component[] createComponentsDirectly(Document content, String extension, Map<?, ?> arg) {
            return exec().createComponentsDirectly(content, extension, arg);
        }

        public Component[] createComponentsDirectly(Reader reader, String extension, Map<?, ?> arg) throws IOException {
            return exec().createComponentsDirectly(reader, extension, arg);
        }

        public String encodeURL(String uri) {
            return exec().encodeURL(uri);
        }

        public Object evaluate(Component comp, String expr, Class expectedType) {
            return exec().evaluate(comp, expr, expectedType);
        }

        public Object evaluate(Page page, String expr, Class expectedType) {
            return exec().evaluate(page, expr, expectedType);
        }

        public void forward(Writer writer, String page, Map<String, ?> params, int mode) throws IOException {
            exec().forward(writer, page, params, mode);

        }

        public void forward(String page) throws IOException {
            exec().forward(page);
        }

        public Map<?, ?> getArg() {
            return exec().getArg();
        }

        public Object getAttribute(String name) {
            return exec().getAttribute(name);
        }

        public Object getAttribute(String name, boolean recurse) {
            return exec().getAttribute(name, recurse);
        }

        public boolean hasAttribute(String name) {
            return exec().hasAttribute(name);
        }

        public boolean hasAttribute(String name, boolean recurse) {
            return exec().hasAttribute(name, recurse);
        }

        public Object setAttribute(String name, Object value, boolean recurse) {
            return exec().setAttribute(name, value, recurse);
        }

        public Object setAttribute(String name, Object value) {
            return exec().setAttribute(name, value);
        }

        public Object removeAttribute(String name, boolean recurse) {
            return exec().removeAttribute(name, recurse);
        }

        public Object removeAttribute(String name) {
            return exec().removeAttribute(name);
        }

        public boolean addScopeListener(ScopeListener listener) {
            return exec().addScopeListener(listener);
        }

        public boolean removeScopeListener(ScopeListener listener) {
            return exec().removeScopeListener(listener);
        }

        public Map<String, Object> getAttributes() {
            return exec().getAttributes();
        }

        public String getContextPath() {
            return exec().getContextPath();
        }

        public Desktop getDesktop() {
            return exec().getDesktop();
        }

        public Evaluator getEvaluator(Page page, Class<? extends ExpressionFactory> expfcls) {
            return exec().getEvaluator(page, expfcls);
        }

        public Evaluator getEvaluator(Component comp, Class<? extends ExpressionFactory> expfcls) {
            return exec().getEvaluator(comp, expfcls);
        }

        public String getLocalAddr() {
            return exec().getLocalAddr();
        }

        public String getLocalName() {
            return exec().getLocalName();
        }

        public int getLocalPort() {
            return exec().getLocalPort();
        }

        public Object getNativeRequest() {
            return exec().getNativeRequest();
        }

        public Object getNativeResponse() {
            return exec().getNativeResponse();
        }

        public PageDefinition getPageDefinition(String uri) {
            return exec().getPageDefinition(uri);
        }

        public PageDefinition getPageDefinitionDirectly(String content, String extension) {
            return exec().getPageDefinitionDirectly(content, extension);
        }

        public PageDefinition getPageDefinitionDirectly(Document content, String extension) {
            return exec().getPageDefinitionDirectly(content, extension);
        }

        public PageDefinition getPageDefinitionDirectly(Reader reader, String extension) throws IOException {
            return exec().getPageDefinitionDirectly(reader, extension);
        }

        public String getParameter(String name) {
            return exec().getParameter(name);
        }

        public Map<String, String[]> getParameterMap() {
            return exec().getParameterMap();
        }

        public String[] getParameterValues(String name) {
            return exec().getParameterValues(name);
        }

        public String getRemoteAddr() {
            return exec().getRemoteAddr();
        }

        public String getRemoteHost() {
            return exec().getRemoteHost();
        }

        public String getRemoteUser() {
            return exec().getRemoteUser();
        }

        public String getServerName() {
            return exec().getServerName();
        }

        public int getServerPort() {
            return exec().getServerPort();
        }

        public String getScheme() {
            return exec().getScheme();
        }

        public String getUserAgent() {
            return exec().getUserAgent();
        }

        public Principal getUserPrincipal() {
            return exec().getUserPrincipal();
        }

        public VariableResolver getVariableResolver() {
            return exec().getVariableResolver();
        }

        public void include(Writer writer, String page, Map<String, ?> params, int mode) throws IOException {
            exec().include(writer, page, params, mode);

        }

        public void include(String page) throws IOException {
            exec().include(page);
        }

        public boolean isAsyncUpdate(Page page) {
            return exec().isAsyncUpdate(page);
        }

        public Double getBrowser(String name) {
            return exec().getBrowser(name);
        }

        public String getBrowser() {
            return exec().getBrowser();
        }

        public boolean isForwarded() {
            return exec().isForwarded();
        }

        public boolean isIncluded() {
            return exec().isIncluded();
        }

        public boolean isUserInRole(String role) {
            return exec().isUserInRole(role);
        }

        public boolean isVoided() {
            return exec().isVoided();
        }

        public void popArg() {
            exec().popArg();
        }

        public void postEvent(Event evt) {
            exec().postEvent(evt);
        }

        public void postEvent(int priority, Event evt) {
            exec().postEvent(priority, evt);
        }

        public void postEvent(int priority, Component realTarget, Event evt) {
            exec().postEvent(priority, realTarget, evt);
        }

        public void pushArg(Map<?, ?> arg) {
            exec().pushArg(arg);
        }

        public void sendRedirect(String uri) {
            exec().sendRedirect(uri);
        }

        public void sendRedirect(String uri, String target) {
            exec().sendRedirect(uri, target);
        }

        public void sendRedirect(String uri, boolean respRedirect) {
            exec().sendRedirect(uri, respRedirect);
        }

        public void setVoided(boolean voided) {
            exec().setVoided(voided);
        }

        public String toAbsoluteURI(String uri, boolean skipInclude) {
            return exec().toAbsoluteURI(uri, skipInclude);
        }

        public void addResponseHeader(String name, String value) {
            exec().addResponseHeader(name, value);
        }

        public void addResponseHeader(String name, Date value) {
            exec().addResponseHeader(name, value);
        }

        public boolean containsResponseHeader(String name) {
            return exec().containsResponseHeader(name);
        }

        public String getHeader(String name) {
            return exec().getHeader(name);
        }

        public Iterable<String> getHeaderNames() {
            return exec().getHeaderNames();
        }

        public Iterable<String> getHeaders(String name) {
            return exec().getHeaders(name);
        }

        public void setResponseHeader(String name, String value) {
            exec().setResponseHeader(name, value);
        }

        public void setResponseHeader(String name, Date value) {
            exec().setResponseHeader(name, value);
        }

        public Session getSession() {
            return exec().getSession();
        }

        public String locate(String path) {
            return exec().locate(path);
        }

        public boolean addVariableResolver(VariableResolver resolver) {
            return exec().addVariableResolver(resolver);
        }

        public boolean removeVariableResolver(VariableResolver resolver) {
            return exec().removeVariableResolver(resolver);
        }

        public boolean hasVariableResolver(VariableResolver resolver) {
            return exec().hasVariableResolver(resolver);
        }

        public boolean hasVariableResolver(Class<? extends VariableResolver> cls) {
            return exec().hasVariableResolver(cls);
        }

        public String toString() {
            return Objects.toString(exec());
        }

        public int hashCode() {
            return Objects.hashCode(exec());
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o instanceof Exec)
                return Objects.equals(exec(), ((Exec) o).exec());
            return Objects.equals(exec(), o);
        }

        public void log(String msg) {
            exec().log(msg);
        }

        public void log(String msg, Throwable ex) {
            exec().log(msg, ex);
        }

    }

    //Proxy to read current requestScope
    private static class RequestScope implements Map<String, Object> {
        protected Map<String, Object> req() {
            return Executions.getCurrent().getAttributes();
        }

        public void clear() {
            req().clear();
        }

        public boolean containsKey(Object key) {
            return req().containsKey(key);
        }

        public boolean containsValue(Object value) {
            return req().containsValue(value);
        }

        public Set<Map.Entry<String, Object>> entrySet() {
            return req().entrySet();
        }

        public Object get(Object key) {
            return req().get(key);
        }

        public boolean isEmpty() {
            return req().isEmpty();
        }

        public Set<String> keySet() {
            return req().keySet();
        }

        public Object put(String key, Object value) {
            return req().put(key, value);
        }

        public void putAll(Map<? extends String, ? extends Object> arg0) {
            req().putAll(arg0);
        }

        public Object remove(Object key) {
            return req().remove(key);
        }

        public int size() {
            return req().size();
        }

        public Collection<Object> values() {
            return req().values();
        }

        public String toString() {
            return Objects.toString(req());
        }

        public int hashCode() {
            return Objects.hashCode(req());
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o instanceof RequestScope)
                return Objects.equals(req(), ((RequestScope) o).req());
            return Objects.equals(req(), o);
        }
    }
}