July 23, 2003

Here is the much anticipated TilesTool, to use with Velocity Tools and Struts 1.1 (smile)

I personally use the nested tiles and tile-controllers features, but I haven't tried other features you might be interested in.

Any comments are welcome on the Velocity Developer's List <velocity-dev@jakarta.apache.org>.

Marinó A. Jónsson


 
{ { { 

package org.apache.velocity.tools.struts;

import java.util.Stack;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletContext;

import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;

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.tools.view.context.ViewContext;
import org.apache.velocity.tools.view.tools.ViewTool;


/**
{{{ * <p>Title: TilesTool</p> 
 * <p>Description: A tool to use struts-tiles with Velocity</p> 
 * <p>Usage: 
 * 
 * Just call $tiles.name_of_tile_definition from the template to insert 
 * the tile. 
 * 
 * $tiles.getString("name_of_tile_attribute") fetches a named attribute-value 
 * from the current tiles-context. 
 * 
 * @author <a href="mailto:marinoj@centrum.is">Marino A. Jonsson</a> 
 * @version $Revision: 1.1 $ $Date: 2003/07/24 04:59:21 $ 
 */ 

public class TilesTool implements ViewTool
{

    protected ViewContext context; 
    protected ServletContext application; 
    protected HttpServletRequest request; 
    protected HttpServletResponse response; 

    /** 
     * 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"); 
        } 

        this.context = (ViewContext)obj; 
        this.request = context.getRequest(); 
        this.response = context.getResponse(); 
        this.application = context.getServletContext(); 
    } 


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

    /** 
     * Fetches a named attribute value from the current tiles-context. 
     * 
     * <p>This is functionally equivalent to  
     * <code><tiles:getAsString name="title" /></code>.</p> 
     * 
     * @param name the name of the tiles-attribute to fetch 
     * @return the attribute value as String 
     */ 
    public String getString(String name) 
    { 
        ComponentContext context = ComponentContext.getContext(request); 
        Object attrValue = context.getAttribute(name); 
        if (attrValue == null) 
        { 
            return null; 
        } 
        return attrValue.toString(); 
    } 


    /** 
     * <p>A generic tiles insert function</p> 
     * 
     * <p>This is functionally equivalent to  
     * <code><tiles:insert attribute="menu" /></code>.</p> 
     * 
     * @param attr - 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 attr) throws Exception 
    { 
        ComponentContext currentContext = ComponentContext.getContext(request); 
        Object attrValue = currentContext.getAttribute(attr.toString()); 
        if (attrValue != null) 
        { 
            return processObjectValue(attrValue); 
        } 
        return processAsDefinitionOrURL(attr.toString()); 
    } 


    /************************** 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, request, 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 && !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 && !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, request); 

            /* Call controller if any */ 
            if (controller != null) 
            { 
                controller.perform(subCompContext, 
                                   request, 
                                   response, 
                                   application); 
            } 
            return parse(page); 
        } 
        finally 
        { 
            popTilesContext(); 
        } 
    } 


    /** 
     * <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 (contextStack == null) 
        { 
            contextStack = new Stack(); 
        } 
        contextStack.push(ComponentContext.getContext(request)); 
    } 


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


    /** 
     * <p>Renders a template</p> 
     * 
     * @param templateName - name of template to be rendered 
     * @throws Exception if it fails 
     * @return the rendered template as a String 
     */ 
    protected String parse(String templateName) throws Exception 
    { 
        StringWriter sw = new StringWriter(); 
        Template template = Velocity.getTemplate(templateName); 
        template.merge(context.getVelocityContext(), sw); 
        return sw.toString(); 
    } 

}

 } } } 
 
  • No labels