You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by so...@apache.org on 2010/01/06 20:18:09 UTC

svn commit: r896628 - in /myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared: application/ view/

Author: sobryan
Date: Wed Jan  6 19:18:08 2010
New Revision: 896628

URL: http://svn.apache.org/viewvc?rev=896628&view=rev
Log:
Changes to expose the ViewDeclarationLanguageBase class and all it's dependencies so that it may be used by the MyFaces Portlet Bridge as well.

Added:
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/InvalidViewIdException.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/ViewHandlerSupport.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/JspViewDeclarationLanguageBase.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewDeclarationLanguageBase.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewResponseWrapper.java

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,396 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.application;
+
+import java.net.MalformedURLException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.faces.application.ViewHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+
+/**
+ * A ViewHandlerSupport implementation for use with standard Java Servlet engines,
+ * ie an engine that supports javax.servlet, and uses a standard web.xml file.
+ *
+ * @author Mathias Broekelmann (latest modification by $Author: lu4242 $)
+ * @version $Revision: 887436 $ $Date: 2009-12-04 16:11:25 -0700 (Fri, 04 Dec 2009) $
+ */
+public class DefaultViewHandlerSupport implements ViewHandlerSupport
+{
+    /**
+     * Identifies the FacesServlet mapping in the current request map.
+     */
+    private static final String CACHED_SERVLET_MAPPING =
+        DefaultViewHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
+
+    //private static final Log log = LogFactory.getLog(DefaultViewHandlerSupport.class);
+    private static final Logger log = Logger.getLogger(DefaultViewHandlerSupport.class.getName());
+
+    public String calculateViewId(FacesContext context, String viewId)
+    {
+        //If no viewId found, don't try to derive it, just continue.
+        if (viewId == null)
+        {
+            return null;
+        }
+        FacesServletMapping mapping = getFacesServletMapping(context);
+        if (mapping == null || mapping.isExtensionMapping())
+        {
+            viewId = handleSuffixMapping(context, viewId);
+        }
+        else if(mapping.isPrefixMapping())
+        {
+            viewId = handlePrefixMapping(viewId,mapping.getPrefix());
+        }
+        else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
+        {
+            throw new InvalidViewIdException(viewId);
+        }
+
+        //if(viewId != null)
+        //{
+        //    return (checkResourceExists(context,viewId) ? viewId : null);
+        //}
+
+        return viewId;    // return null if no physical resource exists
+    }
+    
+    public String calculateAndCheckViewId(FacesContext context, String viewId)
+    {
+        //If no viewId found, don't try to derive it, just continue.
+        if (viewId == null)
+        {
+            return null;
+        }
+        FacesServletMapping mapping = getFacesServletMapping(context);
+        if (mapping == null || mapping.isExtensionMapping())
+        {
+            viewId = handleSuffixMapping(context, viewId);
+        }
+        else if(mapping.isPrefixMapping())
+        {
+            viewId = handlePrefixMapping(viewId,mapping.getPrefix());
+        }
+        else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
+        {
+            throw new InvalidViewIdException(viewId);
+        }
+
+        if(viewId != null)
+        {
+            return (checkResourceExists(context,viewId) ? viewId : null);
+        }
+
+        return viewId;    // return null if no physical resource exists
+    }
+
+    public String calculateActionURL(FacesContext context, String viewId)
+    {
+        if (viewId == null || !viewId.startsWith("/"))
+        {
+            throw new IllegalArgumentException("ViewId must start with a '/': " + viewId);
+        }
+
+        FacesServletMapping mapping = getFacesServletMapping(context);
+        ExternalContext externalContext = context.getExternalContext();
+        String contextPath = externalContext.getRequestContextPath();
+        StringBuilder builder = new StringBuilder(contextPath);
+        if (mapping != null)
+        {
+            if (mapping.isExtensionMapping())
+            {
+                String[] contextSuffixes = getContextSuffix(context); 
+                boolean founded = false;
+                for (String contextSuffix : contextSuffixes)
+                {
+                    if (viewId.endsWith(contextSuffix))
+                    {
+                        builder.append(viewId.substring(0, viewId.indexOf(contextSuffix)));
+                        builder.append(mapping.getExtension());
+                        founded = true;
+                        break;
+                    }
+                }
+                if (!founded)
+                {   
+                    if(viewId.lastIndexOf(".") != -1 )
+                    {
+                        builder.append(viewId.substring(0,viewId.lastIndexOf(".")));
+                    }
+                    else
+                    {
+                        builder.append(viewId);
+                    }
+                    builder.append(contextSuffixes[0]);
+                }
+            }
+            else
+            {
+                builder.append(mapping.getPrefix());
+                builder.append(viewId);
+            }
+        }
+        else
+        {
+            builder.append(viewId);
+        }
+        String calculatedActionURL = builder.toString();
+        if (log.isLoggable(Level.FINEST))
+        {
+            log.finest("Calculated actionURL: '" + calculatedActionURL + "' for viewId: '" + viewId + "'");
+        }
+        return calculatedActionURL;
+    }
+
+    /**
+     * Read the web.xml file that is in the classpath and parse its internals to
+     * figure out how the FacesServlet is mapped for the current webapp.
+     */
+    protected FacesServletMapping getFacesServletMapping(FacesContext context)
+    {
+        Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
+
+        // Has the mapping already been determined during this request?
+        if (!requestMap.containsKey(CACHED_SERVLET_MAPPING))
+        {
+            ExternalContext externalContext = context.getExternalContext();
+            FacesServletMapping mapping =
+                calculateFacesServletMapping(
+                    externalContext.getRequestServletPath(),
+                    externalContext.getRequestPathInfo());
+
+            requestMap.put(CACHED_SERVLET_MAPPING, mapping);
+        }
+
+        return (FacesServletMapping) requestMap.get(CACHED_SERVLET_MAPPING);
+    }
+
+    /**
+     * Determines the mapping of the FacesServlet in the web.xml configuration
+     * file. However, there is no need to actually parse this configuration file
+     * as runtime information is sufficient.
+     *
+     * @param servletPath The servletPath of the current request
+     * @param pathInfo    The pathInfo of the current request
+     * @return the mapping of the FacesServlet in the web.xml configuration file
+     */
+    protected static FacesServletMapping calculateFacesServletMapping(
+        String servletPath, String pathInfo)
+    {
+        if (pathInfo != null)
+        {
+            // If there is a "extra path", it's definitely no extension mapping.
+            // Now we just have to determine the path which has been specified
+            // in the url-pattern, but that's easy as it's the same as the
+            // current servletPath. It doesn't even matter if "/*" has been used
+            // as in this case the servletPath is just an empty string according
+            // to the Servlet Specification (SRV 4.4).
+            return FacesServletMapping.createPrefixMapping(servletPath);
+        }
+        else
+        {
+            // In the case of extension mapping, no "extra path" is available.
+            // Still it's possible that prefix-based mapping has been used.
+            // Actually, if there was an exact match no "extra path"
+            // is available (e.g. if the url-pattern is "/faces/*"
+            // and the request-uri is "/context/faces").
+            int slashPos = servletPath.lastIndexOf('/');
+            int extensionPos = servletPath.lastIndexOf('.');
+            if (extensionPos > -1 && extensionPos > slashPos)
+            {
+                String extension = servletPath.substring(extensionPos);
+                return FacesServletMapping.createExtensionMapping(extension);
+            }
+            else
+            {
+                // There is no extension in the given servletPath and therefore
+                // we assume that it's an exact match using prefix-based mapping.
+                return FacesServletMapping.createPrefixMapping(servletPath);
+            }
+        }
+    }
+
+    protected String[] getContextSuffix(FacesContext context)
+    {
+        String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
+        if (defaultSuffix == null)
+        {
+            defaultSuffix = ViewHandler.DEFAULT_SUFFIX;
+        }
+        return defaultSuffix.split(" ");
+    }
+    
+    protected String getFaceletsContextSuffix(FacesContext context)
+    {
+        String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
+        if (defaultSuffix == null)
+        {
+            defaultSuffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
+        }
+        return defaultSuffix;
+    }
+    
+    
+    
+    protected String[] getFaceletsViewMappings(FacesContext context)
+    {
+        String faceletsViewMappings= context.getExternalContext().getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
+        if(faceletsViewMappings == null)    //consider alias facelets.VIEWMAPPINGS
+        {
+            faceletsViewMappings= context.getExternalContext().getInitParameter("facelets.VIEWMAPPINGS");
+        }
+        
+        return faceletsViewMappings == null ? null : faceletsViewMappings.split(";");
+    }
+
+    /**
+     * Return the normalized viewId according to the algorithm specified in 7.5.2 
+     * by stripping off any number of occurrences of the prefix mapping from the viewId.
+     * <p/>
+     * For example, both /faces/view.xhtml and /faces/faces/faces/view.xhtml would both return view.xhtml
+     * F 
+     */
+    protected String handlePrefixMapping(String viewId, String prefix)
+    {
+        /*  If prefix mapping (such as "/faces/*") is used for FacesServlet, normalize the viewId according to the following
+            algorithm, or its semantic equivalent, and return it.
+               
+            Remove any number of occurrences of the prefix mapping from the viewId. For example, if the incoming value
+            was /faces/faces/faces/view.xhtml the result would be simply view.xhtml.
+         */
+        String uri = viewId;
+        prefix = prefix + '/';  //need to make sure its really /faces/* and not /facesPage.xhtml
+        while (uri.startsWith(prefix)) 
+        {
+            uri = uri.substring(prefix.length() - 1);    //cut off only /faces, leave the trailing '/' char for the next iteration
+        }
+        //now delete any remaining leading '/'
+        // TODO: CJH: I don't think this is correct, considering that getActionURL() expects everything to
+        // start with '/', and in the suffix case we only mess with the suffix and leave leading
+        // slashes alone.  Please review...
+        /*if(uri.startsWith("/"))
+        {
+            uri = uri.substring(1);
+        }*/
+        
+        return uri;
+    }
+    
+    /**
+     * Return the viewId with any non-standard suffix stripped off and replaced with
+     * the default suffix configured for the specified context.
+     * <p/>
+     * For example, an input parameter of "/foo.jsf" may return "/foo.jsp".
+     */
+    protected String handleSuffixMapping(FacesContext context, String requestViewId)
+    {
+        String[] faceletsViewMappings = getFaceletsViewMappings(context);
+        String[] jspDefaultSuffixes = getContextSuffix(context);
+        
+        int slashPos = requestViewId.lastIndexOf('/');
+        int extensionPos = requestViewId.lastIndexOf('.');
+        
+        //Try to locate any resource that match with the expected id
+        for (String defaultSuffix : jspDefaultSuffixes)
+        {
+            StringBuilder builder = new StringBuilder(requestViewId);
+           
+            if (extensionPos > -1 && extensionPos > slashPos)
+            {
+                builder.replace(extensionPos, requestViewId.length(), defaultSuffix);
+            }
+            else
+            {
+                builder.append(defaultSuffix);
+            }
+            String candidateViewId = builder.toString();
+            
+            if( faceletsViewMappings != null && faceletsViewMappings.length > 0 )
+            {
+                for (String mapping : faceletsViewMappings)
+                {
+                    if(mapping.startsWith("/"))
+                    {
+                        continue;   //skip this entry, its a prefix mapping
+                    }
+                    if(mapping.equals(candidateViewId))
+                    {
+                        return candidateViewId;
+                    }
+                    if(mapping.startsWith(".")) //this is a wildcard entry
+                    {
+                        builder.setLength(0); //reset/reuse the builder object 
+                        builder.append(candidateViewId); 
+                        builder.replace(candidateViewId.lastIndexOf('.'), candidateViewId.length(), mapping);
+                        String tempViewId = builder.toString();
+                        if(checkResourceExists(context,tempViewId))
+                            return tempViewId;
+                    }
+                }
+            }
+
+            // forced facelets mappings did not match or there were no entries in faceletsViewMappings array
+            if(checkResourceExists(context,candidateViewId))
+                return candidateViewId;
+                       
+        }
+        
+        //jsp suffixes didn't match, try facelets suffix
+        String faceletsDefaultSuffix = this.getFaceletsContextSuffix(context);
+        StringBuilder builder = new StringBuilder(requestViewId);
+        
+        if (extensionPos > -1 && extensionPos > slashPos)
+        {
+            builder.replace(extensionPos, requestViewId.length(), faceletsDefaultSuffix);
+        }
+        else
+        {
+            builder.append(faceletsDefaultSuffix);
+        }
+        
+        String candidateViewId = builder.toString();
+        if(checkResourceExists(context,candidateViewId))
+            return candidateViewId;
+
+        if(checkResourceExists(context,requestViewId))
+            return requestViewId;
+        
+        return null;
+    }
+    
+    protected boolean checkResourceExists(FacesContext context, String viewId)
+    {
+        try
+        {
+            if (context.getExternalContext().getResource(viewId) != null)
+            {
+                return true;
+            }                                 
+        }
+        catch(MalformedURLException e)
+        {
+            //ignore and move on
+        }     
+        return false;
+    }
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.application;
+
+/**
+ * Represents a mapping entry of the FacesServlet in the web.xml
+ * configuration file.
+ */
+public class FacesServletMapping
+{
+
+    /**
+     * The path ("/faces", for example) which has been specified in the
+     * url-pattern of the FacesServlet mapping.
+     */
+    private String prefix;
+
+    /**
+     * The extension (".jsf", for example) which has been specified in the
+     * url-pattern of the FacesServlet mapping.
+     */
+    private String extension;
+
+    /**
+     * Creates a new FacesServletMapping object using prefix mapping.
+     *
+     * @param path The path ("/faces", for example) which has been specified
+     *             in the url-pattern of the FacesServlet mapping.
+     * @return a newly created FacesServletMapping
+     */
+    public static FacesServletMapping createPrefixMapping(String path)
+    {
+        FacesServletMapping mapping = new FacesServletMapping();
+        mapping.setPrefix(path);
+        return mapping;
+    }
+
+    /**
+     * Creates a new FacesServletMapping object using extension mapping.
+     *
+     * @param path The extension (".jsf", for example) which has been
+     *             specified in the url-pattern of the FacesServlet mapping.
+     * @return a newly created FacesServletMapping
+     */
+    public static FacesServletMapping createExtensionMapping(
+        String extension)
+    {
+        FacesServletMapping mapping = new FacesServletMapping();
+        mapping.setExtension(extension);
+        return mapping;
+    }
+
+    /**
+     * Returns the path ("/faces", for example) which has been specified in
+     * the url-pattern of the FacesServlet mapping. If this mapping is based
+     * on an extension, <code>null</code> will be returned. Note that this
+     * path is not the same as the specified url-pattern as the trailing
+     * "/*" is omitted.
+     *
+     * @return the path which has been specified in the url-pattern
+     */
+    public String getPrefix()
+    {
+        return prefix;
+    }
+
+    /**
+     * Sets the path ("/faces/", for example) which has been specified in
+     * the url-pattern.
+     *
+     * @param path The path which has been specified in the url-pattern
+     */
+    public void setPrefix(String path)
+    {
+        this.prefix = path;
+    }
+
+    /**
+     * Returns the extension (".jsf", for example) which has been specified
+     * in the url-pattern of the FacesServlet mapping. If this mapping is
+     * not based on an extension, <code>null</code> will be returned.
+     *
+     * @return the extension which has been specified in the url-pattern
+     */
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    /**
+     * Sets the extension (".jsf", for example) which has been specified in
+     * the url-pattern of the FacesServlet mapping.
+     *
+     * @param extension The extension which has been specified in the url-pattern
+     */
+    public void setExtension(String extension)
+    {
+        this.extension = extension;
+    }
+
+    /**
+     * Indicates whether this mapping is based on an extension (e.g.
+     * ".jsp").
+     *
+     * @return <code>true</code>, if this mapping is based is on an
+     *         extension, <code>false</code> otherwise
+     */
+    public boolean isExtensionMapping()
+    {
+        return extension != null;
+    }
+
+    /**
+     * Indicates whether this mapping is based on a prefix (e.g.
+     * /faces/*").
+     *
+     * @return <code>true</code>, if this mapping is based is on a
+     *         prefix, <code>false</code> otherwise
+     */
+    public boolean isPrefixMapping()
+    {
+        return prefix != null;
+    }
+    
+    /**
+     * Returns the url-pattern entry for this servlet mapping.
+     *
+     * @return the url-pattern entry for this servlet mapping
+     */
+    public String getUrlPattern()
+    {
+        if (isExtensionMapping())
+        {
+            return "*" + extension;
+        }
+        else
+        {
+            return prefix + "/*";
+        }
+    }
+
+}
\ No newline at end of file

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/InvalidViewIdException.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/InvalidViewIdException.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/InvalidViewIdException.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/InvalidViewIdException.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.application;
+
+/**
+ * @author Mathias Broekelmann (latest modification by $Author: mbr $)
+ * @version $Revision: 517404 $ $Date: 2007-03-12 15:17:24 -0600 (Mon, 12 Mar 2007) $
+ */
+public class InvalidViewIdException extends RuntimeException
+{
+
+    /**
+     * 
+     */
+    public InvalidViewIdException()
+    {
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @param message
+     */
+    public InvalidViewIdException(String message)
+    {
+        super(message);
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @param cause
+     */
+    public InvalidViewIdException(Throwable cause)
+    {
+        super(cause);
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @param message
+     * @param cause
+     */
+    public InvalidViewIdException(String message, Throwable cause)
+    {
+        super(message, cause);
+        // TODO Auto-generated constructor stub
+    }
+
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/ViewHandlerSupport.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/ViewHandlerSupport.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/ViewHandlerSupport.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/application/ViewHandlerSupport.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.application;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * TODO: RENAME - This class is now used by ViewDeclarationLanguageBase
+ * 
+ * A utility class to isolate a ViewHandler implementation from the underlying 
+ * request/response framework.
+ * <p>
+ * For example, an implementation of this interface might support javax.servlet,
+ * javax.portlet, or some other mechanism.
+ *    
+ * @author Mathias Broekelmann (latest modification by $Author: lu4242 $)
+ * @version $Revision: 887436 $ $Date: 2009-12-04 16:11:25 -0700 (Fri, 04 Dec 2009) $
+ */
+public interface ViewHandlerSupport
+{
+    String calculateViewId(FacesContext context, String viewId);
+    
+    String calculateAndCheckViewId(FacesContext context, String viewId);
+
+    /**
+     * Return a string containing a webapp-relative URL that the user can invoke
+     * to render the specified view.
+     * <p>
+     * URLs and ViewIds are not quite the same; for example a url of "/foo.jsf"
+     * or "/faces/foo.jsp" may be needed to access the view "/foo.jsp". 
+     */
+    String calculateActionURL(FacesContext facesContext, String viewId); 
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/JspViewDeclarationLanguageBase.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/JspViewDeclarationLanguageBase.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/JspViewDeclarationLanguageBase.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/JspViewDeclarationLanguageBase.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,351 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.view;
+
+import java.beans.BeanInfo;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Resource;
+import javax.faces.application.StateManager;
+import javax.faces.application.ViewHandler;
+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.faces.view.StateManagementStrategy;
+import javax.faces.view.ViewMetadata;
+
+import org.apache.myfaces.shared.application.DefaultViewHandlerSupport;
+import org.apache.myfaces.shared.application.ViewHandlerSupport;
+import org.apache.myfaces.shared.config.MyfacesConfig;
+import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
+
+
+public abstract class JspViewDeclarationLanguageBase extends ViewDeclarationLanguageBase
+{
+  private static final Logger log = Logger.getLogger(JspViewDeclarationLanguageBase.class.getName());
+  
+  private static final String FORM_STATE_MARKER = "<!-...@-->";
+  private static final String AFTER_VIEW_TAG_CONTENT_PARAM = JspViewDeclarationLanguageBase.class
+              + ".AFTER_VIEW_TAG_CONTENT";
+  private static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
+
+  private ViewHandlerSupport _cachedViewHandlerSupport;
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public BeanInfo getComponentMetadata(FacesContext context, Resource componentResource)
+  {
+      throw new UnsupportedOperationException();
+  }
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Resource getScriptComponentResource(FacesContext context, Resource componentResource)
+  {
+      throw new UnsupportedOperationException();
+  }
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void renderView(FacesContext context, UIViewRoot view) throws IOException
+  {
+      //Try not to use native objects in this class.  Both MyFaces and the bridge
+      //provide implementations of buildView but they do not override this class.
+      checkNull(context, "context");
+      checkNull(view, "view");
+  
+      // do not render the view if the rendered attribute for the view is false
+      if (!view.isRendered())
+      {
+          if (log.isLoggable(Level.FINEST))
+              log.finest("View is not rendered");
+          return;
+      }
+  
+      ExternalContext externalContext = context.getExternalContext();
+  
+      String viewId = context.getViewRoot().getViewId();
+  
+      if (log.isLoggable(Level.FINEST))
+          log.finest("Rendering JSP view: " + viewId);
+  
+  
+      // handle character encoding as of section 2.5.2.2 of JSF 1.1
+      if(null != externalContext.getSession(false))
+      {
+        externalContext.getSessionMap().put(ViewHandler.CHARACTER_ENCODING_KEY, externalContext.getResponseCharacterEncoding());
+      }
+  
+      // render the view in this method (since JSF 1.2)
+      RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+      RenderKit renderKit = renderFactory.getRenderKit(context, view.getRenderKitId());
+  
+      ResponseWriter responseWriter = context.getResponseWriter();
+      if (responseWriter == null)
+      {
+          responseWriter = renderKit.createResponseWriter(externalContext.getResponseOutputWriter(), null, externalContext.getRequestCharacterEncoding());
+          context.setResponseWriter(responseWriter);
+      }
+  
+      ResponseWriter oldResponseWriter = responseWriter;
+      StateMarkerAwareWriter stateAwareWriter = null;
+  
+      StateManager stateManager = context.getApplication().getStateManager();
+      if (stateManager.isSavingStateInClient(context))
+      {
+          stateAwareWriter = new StateMarkerAwareWriter();
+  
+          // Create a new response-writer using as an underlying writer the stateAwareWriter
+          // Effectively, all output will be buffered in the stateAwareWriter so that later
+          // this writer can replace the state-markers with the actual state.
+          responseWriter = oldResponseWriter.cloneWithWriter(stateAwareWriter);
+          context.setResponseWriter(responseWriter);
+      }
+  
+      actuallyRenderView(context, view);
+      
+      if(oldResponseWriter != null)
+      {
+          context.setResponseWriter(oldResponseWriter);    
+      }
+      
+  
+      // We're done with the document - now we can write all content
+      // to the response, properly replacing the state-markers on the way out
+      // by using the stateAwareWriter
+      if (stateManager.isSavingStateInClient(context))
+      {
+          stateAwareWriter.flushToWriter(externalContext.getResponseOutputWriter());
+      }
+      else
+      {
+          stateManager.saveView(context);
+      }
+  
+      // Final step - we output any content in the wrappedResponse response from above to the response,
+      // removing the wrappedResponse response from the request, we don't need it anymore
+      ViewResponseWrapper afterViewTagResponse = (ViewResponseWrapper) externalContext.getRequestMap()
+              .get(AFTER_VIEW_TAG_CONTENT_PARAM);
+      externalContext.getRequestMap().remove(AFTER_VIEW_TAG_CONTENT_PARAM);
+  
+      if (afterViewTagResponse != null)
+      {
+          afterViewTagResponse.flushToWriter(externalContext.getResponseOutputWriter(), externalContext.getResponseCharacterEncoding());
+      }
+  
+      //TODO sobryan: Is this right?
+      context.getResponseWriter().flush();
+  }
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ViewMetadata getViewMetadata(FacesContext context, String viewId)
+  {
+      // Not necessary given that this method always returns null, but staying true to
+      // the spec.
+  
+      checkNull(context, "context");
+      //checkNull(viewId, "viewId");
+  
+      // JSP impl must return null.
+  
+      return null;
+  }
+  
+  protected void setAfterViewTagResponseWrapper(ExternalContext ec, ViewResponseWrapper wrapper)
+  {
+    ec.getRequestMap().put(AFTER_VIEW_TAG_CONTENT_PARAM, wrapper);
+  }
+  
+  /**
+   * Render the view now - properly setting and resetting the response writer
+   */
+  private void actuallyRenderView(FacesContext facesContext, UIViewRoot viewToRender)
+      throws IOException
+  {
+      // Set the new ResponseWriter into the FacesContext, saving the old one aside.
+      ResponseWriter responseWriter = facesContext.getResponseWriter();
+  
+      // Now we actually render the document
+      // Call startDocument() on the ResponseWriter.
+      responseWriter.startDocument();
+  
+      // Call encodeAll() on the UIViewRoot
+      viewToRender.encodeAll(facesContext);
+  
+      // Call endDocument() on the ResponseWriter
+      responseWriter.endDocument();
+  
+      responseWriter.flush();
+  }
+  
+  @Override
+  public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId)
+  {
+      return null;
+  }
+
+  @Override
+  protected String calculateViewId(FacesContext context, String viewId)
+  {
+      if (_cachedViewHandlerSupport == null)
+      {
+          _cachedViewHandlerSupport = new DefaultViewHandlerSupport();
+      }
+  
+      return _cachedViewHandlerSupport.calculateViewId(context, viewId);
+  }
+
+  /**
+   * Writes the response and replaces the state marker tags with the state information for the current context
+   */
+  private static class StateMarkerAwareWriter extends Writer
+  {
+      private StringBuilder buf;
+
+      public StateMarkerAwareWriter()
+      {
+          this.buf = new StringBuilder();
+      }
+
+      @Override
+      public void close() throws IOException
+      {
+      }
+
+      @Override
+      public void flush() throws IOException
+      {
+      }
+
+      @Override
+      public void write(char[] cbuf, int off, int len) throws IOException
+      {
+          if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0))
+          {
+              throw new IndexOutOfBoundsException();
+          }
+          else if (len == 0)
+          {
+              return;
+          }
+          buf.append(cbuf, off, len);
+      }
+
+      public StringBuilder getStringBuilder()
+      {
+          return buf;
+      }
+
+      public void flushToWriter(Writer writer) throws IOException
+      {
+          FacesContext facesContext = FacesContext.getCurrentInstance();
+          StateManager stateManager = facesContext.getApplication().getStateManager();
+
+          StringWriter stateWriter = new StringWriter();
+          ResponseWriter realWriter = facesContext.getResponseWriter();
+          facesContext.setResponseWriter(realWriter.cloneWithWriter(stateWriter));
+
+          Object serializedView = stateManager.saveView(facesContext);
+
+          stateManager.writeState(facesContext, serializedView);
+          facesContext.setResponseWriter(realWriter);
+
+          StringBuilder contentBuffer = getStringBuilder();
+          String state = stateWriter.getBuffer().toString();
+
+          ExternalContext extContext = facesContext.getExternalContext();
+          if (JavascriptUtils.isJavascriptAllowed(extContext)
+                  && MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript())
+          {
+              // If javascript viewstate is enabled no state markers were written
+              write(contentBuffer, 0, contentBuffer.length(), writer);
+              writer.write(state);
+          }
+          else
+          {
+              // If javascript viewstate is disabled state markers must be replaced
+              int lastFormMarkerPos = 0;
+              int formMarkerPos = 0;
+              // Find all state markers and write out actual state instead
+              while ((formMarkerPos = contentBuffer.indexOf(JspViewDeclarationLanguageBase.FORM_STATE_MARKER, formMarkerPos)) > -1)
+              {
+                  // Write content before state marker
+                  write(contentBuffer, lastFormMarkerPos, formMarkerPos, writer);
+                  // Write state and move position in buffer after marker
+                  writer.write(state);
+                  formMarkerPos += JspViewDeclarationLanguageBase.FORM_STATE_MARKER_LEN;
+                  lastFormMarkerPos = formMarkerPos;
+              }
+              // Write content after last state marker
+              if (lastFormMarkerPos < contentBuffer.length())
+              {
+                  write(contentBuffer, lastFormMarkerPos, contentBuffer.length(), writer);
+              }
+          }
+
+      }
+
+      /**
+       * Writes the content of the specified StringBuffer from index <code>beginIndex</code> to index
+       * <code>endIndex - 1</code>.
+       * 
+       * @param contentBuffer
+       *            the <code>StringBuffer</code> to copy content from
+       * @param beginIndex
+       *            the beginning index, inclusive.
+       * @param endIndex
+       *            the ending index, exclusive
+       * @param writer
+       *            the <code>Writer</code> to write to
+       * @throws IOException
+       *             if an error occurs writing to specified <code>Writer</code>
+       */
+      private void write(StringBuilder contentBuffer, int beginIndex, int endIndex, Writer writer) throws IOException
+      {
+          int index = beginIndex;
+          int bufferSize = 2048;
+          char[] bufToWrite = new char[bufferSize];
+
+          while (index < endIndex)
+          {
+              int maxSize = Math.min(bufferSize, endIndex - index);
+
+              contentBuffer.getChars(index, index + maxSize, bufToWrite, 0);
+              writer.write(bufToWrite, 0, maxSize);
+
+              index += bufferSize;
+          }
+      }
+  }
+
+
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewDeclarationLanguageBase.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewDeclarationLanguageBase.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewDeclarationLanguageBase.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewDeclarationLanguageBase.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,147 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.view;
+
+import javax.faces.application.Application;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.faces.view.ViewDeclarationLanguage;
+
+import org.apache.myfaces.shared.application.InvalidViewIdException;
+
+/**
+ * @author Simon Lessard (latest modification by $Author: slessard $)
+ * @version $Revision: 696523 $ $Date: 2009-03-22 15:03:20 -0400 (mer., 17 sept. 2008) $
+ * 
+ * @since 2.0
+ */
+public abstract class ViewDeclarationLanguageBase extends ViewDeclarationLanguage
+{
+    
+    /**
+     * Process the specification required algorithm that is generic to all PDL.
+     * 
+     * @param context
+     * @param viewId
+     */
+    public UIViewRoot createView(FacesContext context, String viewId)
+    {
+        checkNull(context, "context");
+        //checkNull(viewId, "viewId");
+
+        try
+        {
+            viewId = calculateViewId(context, viewId);
+            
+            Application application = context.getApplication();
+
+            // Create a new UIViewRoot object instance using Application.createComponent(UIViewRoot.COMPONENT_TYPE).
+            UIViewRoot newViewRoot = (UIViewRoot) application.createComponent(UIViewRoot.COMPONENT_TYPE);
+            UIViewRoot oldViewRoot = context.getViewRoot();
+            if (oldViewRoot == null)
+            {
+                // If not, this method must call calculateLocale() and calculateRenderKitId(), and store the results
+                // as the values of the locale and renderKitId, proeprties, respectively, of the newly created
+                // UIViewRoot.
+                ViewHandler handler = application.getViewHandler();
+                newViewRoot.setLocale(handler.calculateLocale(context));
+                newViewRoot.setRenderKitId(handler.calculateRenderKitId(context));
+            }
+            else
+            {
+                // If there is an existing UIViewRoot available on the FacesContext, this method must copy its locale
+                // and renderKitId to this new view root
+                newViewRoot.setLocale(oldViewRoot.getLocale());
+                newViewRoot.setRenderKitId(oldViewRoot.getRenderKitId());
+            }
+            
+            // TODO: VALIDATE - The spec is silent on the following line, but I feel bad if I don't set it
+            newViewRoot.setViewId(viewId);
+
+            return newViewRoot;
+        }
+        catch (InvalidViewIdException e)
+        {
+            // If no viewId could be identified, or the viewId is exactly equal to the servlet mapping, 
+            // send the response error code SC_NOT_FOUND with a suitable message to the client.
+            sendSourceNotFound(context, e.getMessage());
+            
+            // TODO: VALIDATE - Spec is silent on the return value when an error was sent
+            return null;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public UIViewRoot restoreView(FacesContext context, String viewId)
+    {
+        checkNull(context, "context");
+        //checkNull(viewId, "viewId");
+
+        Application application = context.getApplication();
+        
+        ViewHandler applicationViewHandler = application.getViewHandler();
+        
+        String renderKitId = applicationViewHandler.calculateRenderKitId(context);
+
+        UIViewRoot viewRoot = application.getStateManager().restoreView(context, viewId, renderKitId);
+
+        return viewRoot;
+    }
+
+    /**
+     * Calculates the effective view identifier for the specified raw view identifier.
+     * 
+     * @param context le current FacesContext
+     * @param viewId the raw view identifier
+     * 
+     * @return the effective view identifier
+     */
+    protected abstract String calculateViewId(FacesContext context, String viewId);
+    
+    /**
+     * Send a source not found to the client. Although it can be considered ok in JSP mode,
+     * I think it's pretty lame to have this kind of requirement at VDL level considering VDL 
+     * represents the page --> JSF tree link, not the transport layer required to send a 
+     * SC_NOT_FOUND.
+     * 
+     * @param context le current FacesContext
+     * @param message the message associated with the error
+     */
+    protected abstract void sendSourceNotFound(FacesContext context, String message);
+    
+    /**
+     * Check if the specified value of a param is <code>null</code>.
+     * 
+     * @param o the parameter's value
+     * @param param the parameter's name
+     * 
+     * @throws NullPointerException if the value is <code>null</code>
+     */
+    protected void checkNull(final Object o, final String param)
+    {
+        if (o == null)
+        {
+            throw new NullPointerException(param + " can not be null.");
+        }
+    }
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewResponseWrapper.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewResponseWrapper.java?rev=896628&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewResponseWrapper.java (added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/view/ViewResponseWrapper.java Wed Jan  6 19:18:08 2010
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.myfaces.shared.view;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public interface ViewResponseWrapper
+{
+  void flushToWriter(Writer writer,String encoding) throws IOException;
+}