FileDocCategorySizeDatePackage
ClassViewHandler.javaAPI DocExample8375Tue Jun 08 11:26:42 BST 2004com.mycompany.jsf.pl

ClassViewHandler.java

package com.mycompany.jsf.pl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.application.StateManager.SerializedView;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import com.mycompany.newsservice.views.SubscribeView;

/**
 * This class is a JSF ViewHandler that works with views created
 * by classes implementing the View interface.
 *
 * @author Hans Bergsten, Gefion Software <hans@gefionsoftware.com>
 * @version 1.0
 */
public class ClassViewHandler extends ViewHandler {
    private static final String STATE_VAR = "com.mycompany.viewState";
    protected ViewHandler origViewHandler;
    private Map views = new HashMap();

    /**
     * Creates an instance and saves a reference to the
     * previously registered ViewHandler.
     */
    public ClassViewHandler(ViewHandler origViewHandler) {
        this.origViewHandler = origViewHandler;
    }

    /**
     * Delegates the call to the previously registered ViewHandler.
     */
    public Locale calculateLocale(FacesContext context) {
        return origViewHandler.calculateLocale(context);
    }

    /**
     * Delegates the call to the previously registered ViewHandler.
     */
    public String calculateRenderKitId(FacesContext context) {
        return origViewHandler.calculateRenderKitId(context);
    }

    /**
     * Delegates the call to the previously registered ViewHandler.
     */
    public String getActionURL(FacesContext context, String viewId) {
        return origViewHandler.getActionURL(context, viewId);
    }

    /**
     * Delegates the call to the previously registered ViewHandler.
     */
    public String getResourceURL(FacesContext context, String path) {
        return origViewHandler.getResourceURL(context, path);
    }

    /**
     * Returns the UIViewRoot for the specified view ID, created
     * by the createViewRoot() method, with the "locale" and 
     * "renderKitId" properties initialized.
     */
    public UIViewRoot createView(FacesContext context, String viewId) {
        String realViewId = viewId;
        if (viewId.indexOf(".") != -1) {
            realViewId = viewId.substring(0, viewId.indexOf("."));
        }

        UIViewRoot viewRoot = createViewRoot(context, realViewId);
        if (viewRoot != null) {
            if (context.getViewRoot() != null) {
                UIViewRoot oldRoot = context.getViewRoot();
                viewRoot.setLocale(oldRoot.getLocale());
                viewRoot.setRenderKitId(oldRoot.getRenderKitId());
            }
            else {
		ViewHandler activeVH = 
		    context.getApplication().getViewHandler();
                viewRoot.setLocale(activeVH.calculateLocale(context));
                viewRoot.setRenderKitId(activeVH.calculateRenderKitId(context));
            }
        }
        return viewRoot;
    }

    /**
     * Gets the view state to save, if any, and puts a reference to
     * the state in a request scope variable where it can be picked
     * up by the writeState() method, and renders the view represented
     * by the provided UIViewRoot instance by calling renderResponse().
     */
    public void renderView(FacesContext context, UIViewRoot viewToRender) 
        throws IOException {

        setupResponseWriter(context);

        StateManager sm = context.getApplication().getStateManager();
        SerializedView state = sm.saveSerializedView(context);
	context.getExternalContext().getRequestMap().put(STATE_VAR, state);

        context.getResponseWriter().startDocument();
        renderResponse(context, viewToRender);
        context.getResponseWriter().endDocument();
    }

    /**
     * Returns the UIViewRoot for the restored view identified by
     * the provided view ID, or null if no state is available.
     */
    public UIViewRoot restoreView(FacesContext context, String viewId) {
        String realViewId = viewId;
        if (viewId.indexOf(".") != -1) {
            realViewId = viewId.substring(0, viewId.indexOf("."));
        }

        String renderKitId = 
	    context.getApplication().getViewHandler().
	    calculateRenderKitId(context);

        StateManager sm = context.getApplication().getStateManager();
        return sm.restoreView(context, realViewId, renderKitId);
    }

    /**
     * Writes the state captured and saved by the renderView() method
     * to the response with the help of the current StateManager.
     */
    public void writeState(FacesContext context) throws IOException {
	SerializedView state = (SerializedView)
	    context.getExternalContext().getRequestMap().get(STATE_VAR);
	if (state != null) {
	    StateManager sm = context.getApplication().getStateManager();
	    sm.writeState(context, state);
	}
    }

    /**
     * Returns the UIViewRoot for the view created by the View instance
     * matching the view ID.
     */
    protected UIViewRoot createViewRoot(FacesContext context, String viewId) {
        UIViewRoot viewRoot = null;
        View view = (View) views.get(viewId);
        if (view == null) {
            if ("/subscribe".equals(viewId)) {
                view = new SubscribeView();
                views.put(viewId, view);
            }
        }
        if (view != null) {
            viewRoot = view.createView(context);
            viewRoot.setViewId(viewId);
        }
        return viewRoot;
    }

    /**
     * Recursively renders the provided component and all its children
     * by calling encodeBegin(), encodeChildren() (if the component
     * renders its children) and encodeEnd().
     */
    protected void renderResponse(FacesContext context, UIComponent component)
        throws IOException {

        component.encodeBegin(context);
        if (component.getRendersChildren()) {
            component.encodeChildren(context);
        }
        else {
            Iterator i = component.getChildren().iterator();
            while (i.hasNext()) {
                renderResponse(context, (UIComponent) i.next());
            }
        }
        component.encodeEnd(context);
    }

    /**
     * Asks the current RenderKit to create a ResponseWriter around
     * the OutputStream for the response body and sets the
     * Content-Type response header to the MIME type selected by
     * the ResponseWriter from the alternatives listed in the
     * Accept request header.
     */
    private void setupResponseWriter(FacesContext context) 
        throws IOException {

        ServletResponse response = (ServletResponse)
            context.getExternalContext().getResponse();
        OutputStream os = response.getOutputStream();
	Map headers = context.getExternalContext().getRequestHeaderMap();
	String acceptHeader = (String) headers.get("Accept");

	// Work-around for JSF 1.0 RI bug: failing to accept "*/*" and
	// and "text/*" as valid replacements for "text/html"
	if (acceptHeader != null && 
	    (acceptHeader.indexOf("*/*") != -1 ||
	     acceptHeader.indexOf("text/*") != -1)) {
	    acceptHeader += ",text/html";
	}

        RenderKitFactory renderFactory = (RenderKitFactory)
            FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
        RenderKit renderKit = 
            renderFactory.getRenderKit(context,
                 context.getViewRoot().getRenderKitId());
        ResponseWriter writer = 
            renderKit.createResponseWriter(new OutputStreamWriter(os),
                acceptHeader, response.getCharacterEncoding());
        context.setResponseWriter(writer);
        response.setContentType(writer.getContentType());
    }
}