/* * Copyright 2004-2005 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.shale.tiles; import java.io.IOException; import java.text.MessageFormat; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.faces.FacesException; import javax.faces.application.ViewHandler; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tiles.ComponentContext; import org.apache.tiles.ComponentDefinition; import org.apache.tiles.DefinitionsFactoryException; import org.apache.tiles.NoSuchDefinitionException; import org.apache.tiles.TilesUtil; import org.apache.tiles.TilesContext; import org.apache.tiles.context.servlet.ServletTilesContext; /** * This view handler strips the suffix off of the view ID and looks * for a tile whose name matches the resulting string. For example, if the * view ID is /tiles/test.jsp, this view handler will look for a tile named * /tiles/test. If the tile is found, it is rendered; otherwise, this view handler * delegates to the default JSF view handler. *
* To render a tile, this view handler first locates the tile by name. Then it * creates or accesses the Tile Context, and stores the tile's attributes * in the context. Finally, it dispatches the request to the tile's layout * by calling JSF'sExternalContext.dispatch(). Layouts typically
* contain <tiles:insert> tags that include dynamic content.
*
* If the request does not reference a tile, this view handler delegates
* view rendering to the default view handler. That means that URLs like this:
* http://localhost:8080/example/index.faces will work as
* expected.
*
* Most of the methods in this class simply delegate to the default view
* handler, which JSF passes to this view handler's constructor. The only
* method that has a meaningful implementation is void
* renderView(FacesContext, UIViewRoot), which renders the current
* view in accordance with the algorithm discussed above.
*
* Note: This Tiles view handler is tied to the standalone
* version of Tiles, which resides in the Struts sandbox. This view handler
* will not work with Struts Tiles.
*/
public class TilesViewHandler extends ViewHandler {
// ------------------------------------------------------------- Constructor
/**
* Stores the reference to the default view handler for later use.
* * @param defaultViewHandler The default view handler */ public TilesViewHandler(ViewHandler defaultViewHandler) { this.defaultViewHandler = defaultViewHandler; } // -------------------------------------------------------- Static Variables /** *MessageFormat used to perform parameter substitution.
Log instance for this class.
*/ private static final Log log = LogFactory.getLog( TilesViewHandler.class.getName()); /** *Message resources for this class.
*/ private static ResourceBundle bundle = ResourceBundle.getBundle("org.apache.shale.tiles.Bundle", Locale.getDefault(), TilesViewHandler.class.getClassLoader()); /** *The default JSF view handler.
*/ private ViewHandler defaultViewHandler = null; // ----------------------------------------------------- ViewHandler Methods /** *Render a view according to the algorithm described in this class's
* description: Based on the view Id of the viewToRender,
* this method either renders a tile or delegates rendering to the default
* view handler, which takes care of business as usual.
Pass through to the default view handler.
* */ public UIViewRoot createView(FacesContext context, String viewId) { return defaultViewHandler.createView(context, viewId); } /** *Pass through to the default view handler.
* */ public Locale calculateLocale(FacesContext context) { return defaultViewHandler.calculateLocale(context); } /** *Pass through to the default view handler.
* */ public String calculateRenderKitId(FacesContext context) { return defaultViewHandler.calculateRenderKitId(context); } /** *Pass through to the default view handler.
* */ public String getActionURL(FacesContext context, String viewId) { return defaultViewHandler.getActionURL(context, viewId); } /** *Pass through to the default view handler.
* */ public String getResourceURL(FacesContext context, String path) { return defaultViewHandler.getResourceURL(context, path); } /** *Pass through to the default view handler.
* */ public UIViewRoot restoreView(FacesContext context, String viewId) { return defaultViewHandler.restoreView(context, viewId); } /** *Pass through to the default view handler.
* */ public void writeState(FacesContext context) throws IOException { defaultViewHandler.writeState(context); } // --------------------------------------------------------- Private Methods /** *Looks up a tile, given a name. If the tile does not exist, and the
* name begins with a slash ('/'), look for a tile
* without the slash. If no tile is found, return null.
Given a view ID, returns the name of the corresponding tile. For * example, for a view ID of /tiles/example/main.jsp, the tile name * returned by this method would be /tiles/example/main.
* * @param viewId The view ID */ private String getTileName(String viewId) { int suffixIndex = viewId.lastIndexOf('.'); return suffixIndex != -1 ? viewId.substring(0, suffixIndex) : viewId; } /** *Dispatches to a tile's layout. Layouts typically contain * <tiles:insert> tags that include content, so dispatching * to the tile's layout will automatically build the tile.
** Before dispatching to the tile, this method sets up the Tile * context.
* * @param externalContext The JSF external context * @param tile The tile definition */ private void dispatchToTile(ExternalContext externalContext, ComponentDefinition tile) throws java.io.IOException { HttpServletRequest servletRequest = (HttpServletRequest) externalContext.getRequest(); ServletContext servletContext = (ServletContext) externalContext.getContext(); TilesContext tilesContext = new ServletTilesContext(servletContext, servletRequest); ComponentContext tileContext = ComponentContext.getContext(tilesContext); if (tileContext == null) { tileContext = new ComponentContext(tile.getAttributes()); ComponentContext.setContext(tileContext, tilesContext); } else tileContext.addMissing(tile.getAttributes()); // dispatch to the tile's layout externalContext.dispatch(tile.getPath()); } }