Package org.apache.velocity.tools.struts

Source Code of org.apache.velocity.tools.struts.TilesTool

/*
* Copyright 2003-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.velocity.tools.struts;

import java.util.Stack;
import java.util.Map;
import java.util.Iterator;

import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.struts.tiles.ComponentContext;
import org.apache.struts.tiles.ComponentDefinition;
import org.apache.struts.tiles.AttributeDefinition;
import org.apache.struts.tiles.DirectStringAttribute;
import org.apache.struts.tiles.DefinitionAttribute;
import org.apache.struts.tiles.DefinitionNameAttribute;
import org.apache.struts.tiles.PathAttribute;
import org.apache.struts.tiles.TilesUtil;
import org.apache.struts.tiles.DefinitionsFactoryException;
import org.apache.struts.tiles.Controller;

import org.apache.velocity.context.Context;
import org.apache.velocity.tools.view.ImportSupport;
import org.apache.velocity.tools.view.context.ViewContext;
import org.apache.velocity.tools.view.tools.ViewTool;

/**
* View tool to use struts-tiles with Velocity.
* <p><pre>
* Template example(s):
*  &lt;!-- insert a tile --&gt;
*  $tiles.myTileDefinition
*
*  &lt;!-- get named attribute value from the current tiles-context --&gt;
*  $tiles.getAttribute("myTileAttribute")
*
*  &lt;!-- import all attributes of the current tiles-context into the velocity-context. --&gt;
*  $tiles.importAttributes()
*
* Toolbox configuration:
* &lt;tool&gt;
*   &lt;key&gt;tiles&lt;/key&gt;
*   &lt;scope&gt;request&lt;/scope&gt;
*   &lt;class&gt;org.apache.velocity.tools.struts.TilesTool&lt;/class&gt;
* &lt;/tool&gt;
* </pre></p>
*
* <p>This tool should only be used in the request scope.</p>
*
* @author <a href="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
* @since VelocityTools 1.1
* @version $Revision: 329695 $ $Date: 2005-10-30 17:34:08 -0800 (Sun, 30 Oct 2005) $
*/
public class TilesTool extends ImportSupport implements ViewTool
{
    protected static final Log LOG = LogFactory.getLog(TilesTool.class);

    static final String PAGE_SCOPE = "page";
    static final String REQUEST_SCOPE = "request";
    static final String SESSION_SCOPE = "session";
    static final String APPLICATION_SCOPE = "application";

    protected Context velocityContext;

    /**
     * A stack to hold ComponentContexts while nested tile-definitions
     * are rendered.
     */
    protected Stack contextStack;

    /******************************* Constructors ****************************/

    /**
     * Default constructor. Tool must be initialized before use.
     */
    public TilesTool() {}

    /**
     * Initializes this tool.
     *
     * @param obj the current ViewContext
     * @throws IllegalArgumentException if the param is not a ViewContext
     */
    public void init(Object obj)
    {
        if (!(obj instanceof ViewContext))
        {
            throw new IllegalArgumentException("Tool can only be initialized with a ViewContext");
        }

        ViewContext viewContext = (ViewContext)obj;
        this.velocityContext = viewContext.getVelocityContext();
        this.request = viewContext.getRequest();
        this.response = viewContext.getResponse();
        this.application = viewContext.getServletContext();
    }

    /***************************** View Helpers ******************************/

    /**
     * A generic tiles insert function.
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:insert attribute="foo" /&gt;</code>.</p>
     *
     * @param obj Can be any of the following:
     *        AttributeDefinition,
     *        tile-definition name,
     *        tile-attribute name,
     *        regular uri.
     *        (checked in that order)
     * @return the rendered template or value as a String
     * @throws Exception on failure
     */
    public String get(Object obj)
    {
        try
        {
            Object value = getCurrentContext().getAttribute(obj.toString());
            if (value != null)
            {
                return processObjectValue(value);
            }
            return processAsDefinitionOrURL(obj.toString());
        }
        catch (Exception e)
        {
            LOG.error("Exeption while rendering Tile " + obj + ": ", e);
            return null;
        }
    }

    /**
     * Fetches a named attribute-value from the current tiles-context.
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:getAsString name="foo" /&gt;</code>.</p>
     *
     * @param name the name of the tiles-attribute to fetch
     * @return attribute value for the named attribute
     */
    public Object getAttribute(String name)
    {
        Object value = getCurrentContext().getAttribute(name);
        if (value == null)
        {
            LOG.warn("Tile attribute '" + name + "' wasn't found in context.");
        }
        return value;
    }

    /**
     * Imports the named attribute-value from the current tiles-context into the
     * current Velocity context.
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:importAttribute name="foo" /&gt;</code>
     *
     * @param name the name of the tiles-attribute to import
     */
    public void importAttribute(String name)
    {
        this.importAttribute(name, PAGE_SCOPE);
    }

    /**
     * Imports the named attribute-value from the current tiles-context into the
     * named context ("page", "request", "session", or "application").
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:importAttribute name="foo" scope="scopeValue" /&gt;</code>
     *
     * @param name the name of the tiles-attribute to import
     * @param scope the named context scope to put the attribute into.
     */
    public void importAttribute(String name, String scope)
    {
        Object value = getCurrentContext().getAttribute(name);
        if (value == null)
        {
            LOG.warn("Tile attribute '" + name + "' wasn't found in context.");
        }

        if (scope.equals(PAGE_SCOPE))
        {
            velocityContext.put(name, value);
        }
        else if (scope.equals(REQUEST_SCOPE))
        {
            request.setAttribute(name, value);
        }
        else if (scope.equals(SESSION_SCOPE))
        {
            request.getSession().setAttribute(name, value);
        }
        else if (scope.equals(APPLICATION_SCOPE))
        {
            application.setAttribute(name, value);
        }
    }

    /**
     * Imports all attributes in the current tiles-context into the
     * current velocity-context.
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:importAttribute /&gt;</code>.</p>
     */
    public void importAttributes()
    {
        this.importAttributes(PAGE_SCOPE);
    }

    /**
     * Imports all attributes in the current tiles-context into the named
     * context ("page", "request", "session", or "application").
     *
     * <p>This is functionally equivalent to
     * <code>&lt;tiles:importAttribute scope="scopeValue" /&gt;</code>.</p>
     *
     * @param scope the named context scope to put the attributes into.
     */
    public void importAttributes(String scope)
    {
        ComponentContext context = getCurrentContext();
        Iterator names = context.getAttributeNames();

        if (scope.equals(PAGE_SCOPE))
        {
            while (names.hasNext())
            {
                String name = (String)names.next();
                velocityContext.put(name, context.getAttribute(name));
            }
        }
        else if (scope.equals(REQUEST_SCOPE))
        {
            while (names.hasNext())
            {
                String name = (String)names.next();
                request.setAttribute(name, context.getAttribute(name));
            }
        }
        else if (scope.equals(SESSION_SCOPE))
        {
            HttpSession session = request.getSession();
            while (names.hasNext())
            {
                String name = (String)names.next();
                session.setAttribute(name, context.getAttribute(name));
            }
        }
        else if (scope.equals(APPLICATION_SCOPE))
        {
            while (names.hasNext())
            {
                String name = (String)names.next();
                application.setAttribute(name, context.getAttribute(name));
            }
        }
    }


    /************************** Protected Methods ****************************/

    /**
     * Process an object retrieved as a bean or attribute.
     *
     * @param value - Object can be a typed attribute, a String, or anything
     *        else. If typed attribute, use associated type. Otherwise, apply
     *        toString() on object, and use returned string as a name.
     * @throws Exception - Throws by underlying nested call to
     *         processDefinitionName()
     * @return the fully processed value as String
     */
    protected String processObjectValue(Object value) throws Exception
    {
        /* First, check if value is one of the Typed Attribute */
        if (value instanceof AttributeDefinition)
        {
            /* We have a type => return appropriate IncludeType */
            return processTypedAttribute((AttributeDefinition)value);
        }
        else if (value instanceof ComponentDefinition)
        {
            return processDefinition((ComponentDefinition)value);
        }
        /* Value must denote a valid String */
        return processAsDefinitionOrURL(value.toString());
    }

    /**
     * Process typed attribute according to its type.
     *
     * @param value Typed attribute to process.
     * @return the fully processed attribute value as String.
     * @throws Exception - Throws by underlying nested call to processDefinitionName()
     */
    protected String processTypedAttribute(AttributeDefinition value)
        throws Exception
    {
        if (value instanceof DirectStringAttribute)
        {
            return (String)value.getValue();
        }
        else if (value instanceof DefinitionAttribute)
        {
            return processDefinition((ComponentDefinition)value.getValue());
        }
        else if (value instanceof DefinitionNameAttribute)
        {
            return processAsDefinitionOrURL((String)value.getValue());
        }
        /* else if( value instanceof PathAttribute ) */
        return doInsert((String)value.getValue(), null, null);
    }

    /**
     * Try to process name as a definition, or as an URL if not found.
     *
     * @param name Name to process.
     * @return the fully processed definition or URL
     * @throws Exception
     */
    protected String processAsDefinitionOrURL(String name) throws Exception
    {
        try
        {
            ComponentDefinition definition =
                    TilesUtil.getDefinition(name, this.request, this.application);
            if (definition != null)
            {
                return processDefinition(definition);
            }
        }
        catch (DefinitionsFactoryException ex)
        {
        /* silently failed, because we can choose to not define a factory. */
        }
        /* no definition found, try as url */
        return processUrl(name);
    }

    /**
     * End of Process for definition.
     *
     * @param definition Definition to process.
     * @return the fully processed definition.
     * @throws Exception from InstantiationException Can't create requested controller
     */
    protected String processDefinition(ComponentDefinition definition) throws
            Exception
    {
        Controller controller = null;
        try
        {
            controller = definition.getOrCreateController();

            String role = definition.getRole();
            String page = definition.getTemplate();

            return doInsert(definition.getAttributes(),
                            page,
                            role,
                            controller);
        }
        catch (InstantiationException ex)
        {
            throw new Exception(ex.getMessage());
        }
    }

    /**
     * Processes an url
     *
     * @param url the URI to process.
     * @return the rendered template as String.
     * @throws Exception
     */
    protected String processUrl(String url) throws Exception
    {
        return doInsert(url, null, null);
    }

    /**
     * Use this if there is no nested tile.
     *
     * @param page the page to process.
     * @param role possible user-role
     * @param controller possible tiles-controller
     * @return the rendered template as String.
     * @throws Exception
     */
    protected String doInsert(String page, String role, Controller controller) throws
            Exception
    {
        if (role != null && !this.request.isUserInRole(role))
        {
            return null;
        }

        ComponentContext subCompContext = new ComponentContext();
        return doInsert(subCompContext, page, role, controller);
    }

    /**
     * Use this if there is a nested tile.
     *
     * @param attributes attributes for the sub-context
     * @param page the page to process.
     * @param role possible user-role
     * @param controller possible tiles-controller
     * @return the rendered template as String.
     * @throws Exception
     */
    protected String doInsert(Map attributes,
                              String page,
                              String role,
                              Controller controller) throws Exception
    {
        if (role != null && !this.request.isUserInRole(role))
        {
            return null;
        }

        ComponentContext subCompContext = new ComponentContext(attributes);
        return doInsert(subCompContext, page, role, controller);
    }

    /**
     * An extension of the other two doInsert functions
     *
     * @param subCompContext the sub-context to set in scope when the
     *        template is rendered.
     * @param page the page to process.
     * @param role possible user-role
     * @param controller possible tiles-controller
     * @return the rendered template as String.
     * @throws Exception
     */
    protected String doInsert(ComponentContext subCompContext,
                              String page,
                              String role,
                              Controller controller) throws Exception
    {
        pushTilesContext();
        try
        {
            ComponentContext.setContext(subCompContext, this.request);

            /* Call controller if any */
            if (controller != null)
            {
                controller.execute(subCompContext,
                                   this.request,
                                   this.response,
                                   this.application);
            }
            /* pass things off to ImportSupport */
            return this.acquireString(page);
        }
        finally
        {
            popTilesContext();
        }
    }

    /**
     * Retrieve the current tiles component context.
     * This is pretty much just a convenience method.
     */
    protected ComponentContext getCurrentContext()
    {
        return ComponentContext.getContext(this.request);
    }

    /**
     * <p>pushes the current tiles context onto the context-stack.
     * preserving the context is necessary so that a sub-context can be
     * put into request scope and lower level tiles can be rendered</p>
     */
    protected void pushTilesContext()
    {
        if (this.contextStack == null)
        {
            this.contextStack = new Stack();
        }
        contextStack.push(getCurrentContext());
    }

    /**
     * Pops the tiles sub-context off the context-stack after the lower level
     * tiles have been rendered.
     */
    protected void popTilesContext()
    {
        ComponentContext context = (ComponentContext)this.contextStack.pop();
        ComponentContext.setContext(context, this.request);
    }

}
TOP

Related Classes of org.apache.velocity.tools.struts.TilesTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.