You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ma...@apache.org on 2006/08/01 19:44:01 UTC

svn commit: r427657 [21/42] - in /myfaces: core/trunk/api/src/main/java/javax/faces/component/ core/trunk/api/src/test/java/javax/faces/ core/trunk/api/src/test/java/javax/faces/application/ core/trunk/api/src/test/java/javax/faces/component/ core/trun...

Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/renderkit/html/util/DefaultAddResource.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/renderkit/html/util/DefaultAddResource.java?rev=427657&r1=427656&r2=427657&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/renderkit/html/util/DefaultAddResource.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/renderkit/html/util/DefaultAddResource.java Tue Aug  1 10:43:28 2006
@@ -1,1170 +1,1170 @@
-/*
- * Copyright 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.myfaces.renderkit.html.util;
-
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
-import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
-import org.apache.myfaces.shared_tomahawk.util.ClassUtils;
-
-import javax.faces.FacesException;
-import javax.faces.context.FacesContext;
-import javax.faces.context.ResponseWriter;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This is a utility class to render link to resources used by custom components.
- * Mostly used to avoid having to include [script src="..."][/script]
- * in the head of the pages before using a component.
- * <p/>
- * When used together with the ExtensionsFilter, this class can allow components
- * in the body of a page to emit script and stylesheet references into the page
- * head section. The relevant methods on this object simply queue the changes,
- * and when the page is complete the ExtensionsFilter calls back into this
- * class to allow it to insert the commands into the buffered response.
- * <p/>
- * This class also works with the ExtensionsFilter to allow components to
- * emit references to javascript/css/etc which are bundled in the component's
- * jar file. Special URLs are generated which the ExtensionsFilter will later
- * handle by retrieving the specified resource from the classpath.
- * <p/>
- * The special URL format is:
- * <pre>
- * {contextPath}/faces/myFacesExtensionResource/
- *    {resourceLoaderName}/{cacheKey}/{resourceURI}
- * </pre>
- * Where:
- * <ul>
- * <li> {contextPath} is the context path of the current webapp
- * <li> {resourceLoaderName} is the fully-qualified name of a class which
- * implements the ResourceLoader interface. When a browser app sends a request
- * for the specified resource, an instance of the specified ResourceLoader class
- * will be created and passed the resourceURI part of the URL for resolving to the
- * actual resource to be served back. The standard MyFaces ResourceLoader
- * implementation only serves resources for files stored beneath path
- * org/apache/myfaces/custom in the classpath but non-myfaces code can provide their
- * own ResourceLoader implementations.
- * </ul>
- *
- * @author Sylvain Vieujot (latest modification by $Author: mmarinschek $)
- * @version $Revision: 371739 373827 $ $Date: 2006-01-31 14:50:35 +0000 (Tue, 31 Jan 2006) $
- */
-public class DefaultAddResource implements AddResource
-{
-    private static final String PATH_SEPARATOR = "/";
-
-    protected static final Log log = LogFactory.getLog(DefaultAddResource.class);
-
-    private static final String RESOURCE_VIRTUAL_PATH = "/faces/myFacesExtensionResource";
-
-    private static final String RESOURCES_CACHE_KEY = AddResource.class.getName() + ".CACHE_KEY";
-
-    protected String _contextPath;
-
-    private StringBuffer originalResponse;
-
-    private Set headerBeginInfo;
-    private Set bodyEndInfo;
-    private Set bodyOnloadInfo;
-
-
-    protected boolean parserCalled = false;
-    protected int headerInsertPosition = -1;
-    protected int bodyInsertPosition = -1;
-    protected int beforeBodyPosition = -1;
-    protected int afterBodyContentInsertPosition = -1;
-    protected int beforeBodyEndPosition = -1;
-
-    protected DefaultAddResource()
-    {
-    }
-
-    /**
-     * the context path for the web-app.<br />
-     * You can set the context path only once, every subsequent set will throw an SecurityException
-     */
-    public void setContextPath(String contextPath)
-    {
-        if (_contextPath != null)
-        {
-            throw new SecurityException("context path already set");
-        }
-
-        _contextPath = contextPath;
-    }
-
-    // Methods to add resources
-
-    /**
-     * Insert a [script src="url"] entry at the current location in the response.
-     * The resource is expected to be in the classpath, at the same location as the
-     * specified component + "/resource".
-     * <p/>
-     * Example: when customComponent is class example.Widget, and
-     * resourceName is script.js, the resource will be retrieved from
-     * "example/Widget/resource/script.js" in the classpath.
-     */
-    public void addJavaScriptHere(FacesContext context, Class myfacesCustomComponent,
-                                  String resourceName) throws IOException
-    {
-        addJavaScriptHere(context, new MyFacesResourceHandler(myfacesCustomComponent, resourceName));
-    }
-
-    /**
-     * Insert a [script src="url"] entry at the current location in the response.
-     *
-     * @param uri is the location of the desired resource, relative to the base
-     *            directory of the webapp (ie its contextPath).
-     */
-    public void addJavaScriptHere(FacesContext context, String uri) throws IOException
-    {
-        ResponseWriter writer = context.getResponseWriter();
-
-        writer.startElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM, null);
-        writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-        String src = context.getExternalContext().encodeResourceURL(getResourceUri(context, uri));
-        writer.writeURIAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SRC_ATTR, src, null);
-        writer.endElement(HTML.SCRIPT_ELEM);
-    }
-
-	public void addJavaScriptHerePlain(FacesContext context, String uri) throws IOException
-	{
-        ResponseWriter writer = context.getResponseWriter();
-
-        writer.startElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM, null);
-        writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-        String src = getResourceUri(context, uri);
-        writer.writeURIAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SRC_ATTR, src, null);
-        writer.endElement(HTML.SCRIPT_ELEM);
-	}
-	
-    /**
-     * Insert a [script src="url"] entry at the current location in the response.
-     *
-     * @param context
-     * @param resourceHandler is an object which specifies exactly how to build the url
-     *                        that is emitted into the script tag. Code which needs to generate URLs in ways
-     *                        that this class does not support by default can implement a custom ResourceHandler.
-     * @throws IOException
-     */
-    public void addJavaScriptHere(FacesContext context, ResourceHandler resourceHandler)
-            throws IOException
-    {
-        validateResourceHandler(resourceHandler);
-
-        ResponseWriter writer = context.getResponseWriter();
-
-        writer.startElement(HTML.SCRIPT_ELEM, null);
-        writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-        String src = context.getExternalContext().encodeResourceURL(
-                getResourceUri(context, resourceHandler));
-        writer.writeURIAttribute(HTML.SRC_ATTR, src, null);
-        writer.endElement(HTML.SCRIPT_ELEM);
-    }
-
-    public void addResourceHere(FacesContext context, ResourceHandler resourceHandler)
-            throws IOException
-    {
-        validateResourceHandler(resourceHandler);
-
-        String path = getResourceUri(context, resourceHandler);
-        ResponseWriter writer = context.getResponseWriter();
-        writer.write(context.getExternalContext().encodeResourceURL(path));
-    }
-
-    /**
-     * Verify that the resource handler is acceptable. Null is not
-     * valid, and the getResourceLoaderClass method must return a
-     * Class object whose instances implements the ResourceLoader
-     * interface.
-     *
-     * @param resourceHandler
-     */
-    protected void validateResourceHandler(ResourceHandler resourceHandler)
-    {
-        if (resourceHandler == null)
-        {
-            throw new IllegalArgumentException("ResourceHandler is null");
-        }
-        validateResourceLoader(resourceHandler.getResourceLoaderClass());
-    }
-
-    /**
-     * Given a Class object, verify that the instances of that class
-     * implement the ResourceLoader interface.
-     *
-     * @param resourceloader
-     */
-    protected void validateResourceLoader(Class resourceloader)
-    {
-        if (!ResourceLoader.class.isAssignableFrom(resourceloader))
-        {
-            throw new FacesException("Class " + resourceloader.getName() + " must implement "
-                    + ResourceLoader.class.getName());
-        }
-    }
-
-    /**
-     * Adds the given Javascript resource to the document header at the specified
-     * document positioy by supplying a resourcehandler instance.
-     * <p/>
-     * Use this method to have full control about building the reference url
-     * to identify the resource and to customize how the resource is
-     * written to the response. In most cases, however, one of the convenience
-     * methods on this class can be used without requiring a custom ResourceHandler
-     * to be provided.
-     * <p/>
-     * If the script has already been referenced, it's added only once.
-     * <p/>
-     * Note that this method <i>queues</i> the javascript for insertion, and that
-     * the script is inserted into the buffered response by the ExtensionsFilter
-     * after the page is complete.
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
-                                        ResourceHandler resourceHandler)
-    {
-        addJavaScriptAtPosition(context, position, resourceHandler, false);
-    }
-
-    /**
-     * Insert a [script src="url"] entry into the document header at the
-     * specified document position. If the script has already been
-     * referenced, it's added only once.
-     * <p/>
-     * The resource is expected to be in the classpath, at the same location as the
-     * specified component + "/resource".
-     * <p/>
-     * Example: when customComponent is class example.Widget, and
-     * resourceName is script.js, the resource will be retrieved from
-     * "example/Widget/resource/script.js" in the classpath.
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
-                                        Class myfacesCustomComponent, String resourceName)
-    {
-        addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
-                myfacesCustomComponent, resourceName));
-    }
-    
-	public void addJavaScriptAtPositionPlain(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
-	{
-        addJavaScriptAtPosition(context, position,
-        		new MyFacesResourceHandler(myfacesCustomComponent, resourceName),
-        		false, false);
-	}
-	
-
-    /**
-     * Insert a [script src="url"] entry into the document header at the
-     * specified document position. If the script has already been
-     * referenced, it's added only once.
-     *
-     * @param defer specifies whether the html attribute "defer" is set on the
-     *              generated script tag. If this is true then the browser will continue
-     *              processing the html page without waiting for the specified script to
-     *              load and be run.
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
-                                        Class myfacesCustomComponent, String resourceName, boolean defer)
-    {
-        addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
-                myfacesCustomComponent, resourceName), defer);
-    }
-
-    /**
-     * Insert a [script src="url"] entry into the document header at the
-     * specified document position. If the script has already been
-     * referenced, it's added only once.
-     *
-     * @param uri is the location of the desired resource, relative to the base
-     *            directory of the webapp (ie its contextPath).
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri)
-    {
-        addJavaScriptAtPosition(context, position, uri, false);
-    }
-
-    /**
-     * Adds the given Javascript resource at the specified document position.
-     * If the script has already been referenced, it's added only once.
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri,
-                                        boolean defer)
-    {
-        addPositionedInfo(position, getScriptInstance(context, uri, defer));
-    }
-
-    public void addJavaScriptToBodyTag(FacesContext context, String javascriptEventName,
-                                       String addedJavaScript)
-    {
-        AttributeInfo info = new AttributeInfo();
-        info.setAttributeName(javascriptEventName);
-        info.setAttributeValue(addedJavaScript);
-
-        addPositionedInfo(BODY_ONLOAD, info);
-    }
-
-    /**
-     * Adds the given Javascript resource at the specified document position.
-     * If the script has already been referenced, it's added only once.
-     */
-    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
-                                        ResourceHandler resourceHandler, boolean defer)
-    {
-        validateResourceHandler(resourceHandler);
-        addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer));
-    }
-
-    private void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
-            ResourceHandler resourceHandler, boolean defer, boolean encodeUrl)
-	{
-		validateResourceHandler(resourceHandler);
-		addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer, encodeUrl));
-	}
-    
-    /**
-     * Adds the given Style Sheet at the specified document position.
-     * If the style sheet has already been referenced, it's added only once.
-     */
-    public void addStyleSheet(FacesContext context, ResourcePosition position,
-                              Class myfacesCustomComponent, String resourceName)
-    {
-        addStyleSheet(context, position, new MyFacesResourceHandler(myfacesCustomComponent,
-                resourceName));
-    }
-
-    /**
-     * Adds the given Style Sheet at the specified document position.
-     * If the style sheet has already been referenced, it's added only once.
-     */
-    public void addStyleSheet(FacesContext context, ResourcePosition position, String uri)
-    {
-        addPositionedInfo(position, getStyleInstance(context, uri));
-    }
-
-    /**
-     * Adds the given Style Sheet at the specified document position.
-     * If the style sheet has already been referenced, it's added only once.
-     */
-    public void addStyleSheet(FacesContext context, ResourcePosition position,
-                              ResourceHandler resourceHandler)
-    {
-        validateResourceHandler(resourceHandler);
-        addPositionedInfo(position, getStyleInstance(context, resourceHandler));
-    }
-
-    /**
-     * Adds the given Inline Style at the specified document position.
-     */
-    public void addInlineStyleAtPosition(FacesContext context, ResourcePosition position, String inlineStyle)
-    {
-        addPositionedInfo(position, getInlineStyleInstance(inlineStyle));
-    }
-
-    /**
-     * Adds the given Inline Script at the specified document position.
-     */
-    public void addInlineScriptAtPosition(FacesContext context, ResourcePosition position,
-                                          String inlineScript)
-    {
-        addPositionedInfo(position, getInlineScriptInstance(inlineScript));
-    }
-
-    public String getResourceUri(FacesContext context, Class myfacesCustomComponent,
-                                 String resource, boolean withContextPath)
-    {
-        return getResourceUri(context,
-                new MyFacesResourceHandler(myfacesCustomComponent, resource), withContextPath);
-    }
-
-    public String getResourceUri(FacesContext context, Class myfacesCustomComponent, String resource)
-    {
-        return getResourceUri(context, new MyFacesResourceHandler(myfacesCustomComponent, resource));
-    }
-
-    /**
-     * Get the Path used to retrieve an resource.
-     */
-    public String getResourceUri(FacesContext context, ResourceHandler resourceHandler)
-    {
-        String uri = resourceHandler.getResourceUri(context);
-        if (uri == null)
-        {
-            return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true);
-        }
-        return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true) + uri;
-    }
-
-    /**
-     * Get the Path used to retrieve an resource.
-     */
-    public String getResourceUri(FacesContext context, ResourceHandler resourceHandler,
-                                 boolean withContextPath)
-    {
-        String uri = resourceHandler.getResourceUri(context);
-        if (uri == null)
-        {
-            return getResourceUri(context, resourceHandler.getResourceLoaderClass(),
-                    withContextPath);
-        }
-        return getResourceUri(context, resourceHandler.getResourceLoaderClass(), withContextPath)
-                + uri;
-    }
-
-    /**
-     * Get the Path used to retrieve an resource.
-     */
-    public String getResourceUri(FacesContext context, String uri)
-    {
-        return getResourceUri(context, uri, true);
-    }
-
-    /**
-     * Get the Path used to retrieve an resource.
-     */
-    public String getResourceUri(FacesContext context, String uri, boolean withContextPath)
-    {
-        if (withContextPath)
-        {
-            return context.getApplication().getViewHandler().getResourceURL(context, uri);
-        }
-        return uri;
-    }
-
-    /**
-     * Get the Path used to retrieve an resource.
-     */
-    protected String getResourceUri(FacesContext context, Class resourceLoader,
-                                    boolean withContextPath)
-    {
-        StringBuffer sb = new StringBuffer(200);
-        sb.append(RESOURCE_VIRTUAL_PATH);
-        sb.append(PATH_SEPARATOR);
-        sb.append(resourceLoader.getName());
-        sb.append(PATH_SEPARATOR);
-        sb.append(getCacheKey(context));
-        sb.append(PATH_SEPARATOR);
-        return getResourceUri(context, sb.toString(), withContextPath);
-    }
-
-    /**
-     * Return a value used in the {cacheKey} part of a generated URL for a
-     * resource reference.
-     * <p/>
-     * Caching in browsers normally works by having files served to them
-     * include last-modified and expiry-time http headers. Until the expiry
-     * time is reached, a browser will silently use its cached version. After
-     * the expiry time, it will send a "get if modified since {time}" message,
-     * where {time} is the last-modified header from the version it has cached.
-     * <p/>
-     * Unfortunately this scheme only works well for resources represented as
-     * plain files on disk, where the webserver can easily and efficiently see
-     * the last-modified time of the resource file. When that query has to be
-     * processed by a servlet that doesn't scale well, even when it is possible
-     * to determine the resource's last-modified date from servlet code.
-     * <p/>
-     * Fortunately, for the AddResource class a static resource is only ever
-     * accessed because a URL was embedded by this class in a dynamic page.
-     * This makes it possible to implement caching by instead marking every
-     * resource served with a very long expiry time, but forcing the URL that
-     * points to the resource to change whenever the old cached version becomes
-     * invalid; the browser effectively thinks it is fetching a different
-     * resource that it hasn't seen before. This is implemented by embedding
-     * a "cache key" in the generated URL.
-     * <p/>
-     * Rather than using the actual modification date of a resource as the
-     * cache key, we simply use the webapp deployment time. This means that all
-     * data cached by browsers will become invalid after a webapp deploy (all
-     * the urls to the resources change). It also means that changes that occur
-     * to a resource <i>without</i> a webapp redeploy will not be seen by browsers.
-     */
-    protected long getCacheKey(FacesContext context)
-    {
-        // cache key is hold in application scope so it is recreated on redeploying the webapp.
-        Map applicationMap = context.getExternalContext().getApplicationMap();
-        Long cacheKey = (Long) applicationMap.get(RESOURCES_CACHE_KEY);
-        if (cacheKey == null)
-        {
-            cacheKey = new Long(System.currentTimeMillis() / 100000);
-            applicationMap.put(RESOURCES_CACHE_KEY, cacheKey);
-        }
-        return cacheKey.longValue();
-    }
-
-    public boolean isResourceUri(HttpServletRequest request)
-    {
-        String path;
-        if (_contextPath != null)
-        {
-            path = _contextPath + RESOURCE_VIRTUAL_PATH;
-        }
-        else
-        {
-            path = RESOURCE_VIRTUAL_PATH;
-        }
-        return request.getRequestURI().startsWith(path);
-    }
-
-    private Class getClass(String className) throws ClassNotFoundException
-    {
-        Class clazz = ClassUtils.classForName(className);
-        validateResourceLoader(clazz);
-        return clazz;
-    }
-
-    public void serveResource(ServletContext context, HttpServletRequest request,
-                              HttpServletResponse response) throws IOException
-    {
-        String pathInfo = request.getPathInfo();
-        String uri = request.getContextPath() + request.getServletPath()
-                + (pathInfo == null ? "" : pathInfo);
-        String classNameStartsAfter = RESOURCE_VIRTUAL_PATH + '/';
-
-        int posStartClassName = uri.indexOf(classNameStartsAfter) + classNameStartsAfter.length();
-        int posEndClassName = uri.indexOf(PATH_SEPARATOR, posStartClassName);
-        String className = uri.substring(posStartClassName, posEndClassName);
-        int posEndCacheKey = uri.indexOf(PATH_SEPARATOR, posEndClassName + 1);
-        String resourceUri = null;
-        if (posEndCacheKey + 1 < uri.length())
-        {
-            resourceUri = uri.substring(posEndCacheKey + 1);
-        }
-        try
-        {
-            Class resourceLoader = getClass(className);
-            validateResourceLoader(resourceLoader);
-            ((ResourceLoader) resourceLoader.newInstance()).serveResource(context, request,
-                    response, resourceUri);
-            response.flushBuffer();
-        }
-        catch (ClassNotFoundException e)
-        {
-            log.error("Could not find class for name: " + className, e);
-            response.sendError(HttpServletResponse.SC_NOT_FOUND,
-                    "Could not find resourceloader class for name: " + className);
-        }
-        catch (InstantiationException e)
-        {
-            log.error("Could not instantiate class for name: " + className, e);
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-                    "Could not instantiate resourceloader class for name: " + className);
-        }
-        catch (IllegalAccessException e)
-        {
-            log.error("Could not access class for name: " + className, e);
-            response.sendError(HttpServletResponse.SC_FORBIDDEN,
-                    "Could not access resourceloader class for name: " + className);
-        }
-        catch (Throwable e)
-        {
-            log.error("Error while serving resource: " + resourceUri + ", message : " + e.getMessage(), e);
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        }
-    }
-
-    // Positioned stuffs
-
-    protected Set getHeaderBeginInfos()
-    {
-        if (headerBeginInfo == null)
-        {
-            headerBeginInfo = new LinkedHashSet();
-        }
-        return headerBeginInfo;
-    }
-
-    protected Set getBodyEndInfos()
-    {
-        if (bodyEndInfo == null)
-        {
-            bodyEndInfo = new LinkedHashSet();
-        }
-        return bodyEndInfo;
-    }
-
-    protected Set getBodyOnloadInfos()
-    {
-        if (bodyOnloadInfo == null)
-        {
-            bodyOnloadInfo = new LinkedHashSet();
-        }
-        return bodyOnloadInfo;
-    }
-
-    private void addPositionedInfo(ResourcePosition position, PositionedInfo info)
-    {
-        if (HEADER_BEGIN.equals(position))
-        {
-            Set set = getHeaderBeginInfos();
-            set.add(info);
-        }
-        else if (BODY_END.equals(position))
-        {
-            Set set = getBodyEndInfos();
-            set.add(info);
-
-        }
-        else if (BODY_ONLOAD.equals(position))
-        {
-            Set set = getBodyOnloadInfos();
-            set.add(info);
-        }
-    }
-
-    public boolean hasHeaderBeginInfos()
-    {
-        return headerBeginInfo != null;
-    }
-
-    /**
-     * Parses the response to mark the positions where code will be inserted
-     */
-    public void parseResponse(HttpServletRequest request, String bufferedResponse,
-                              HttpServletResponse response)
-    {
-
-        originalResponse = new StringBuffer(bufferedResponse);
-
-        ParseCallbackListener l = new ParseCallbackListener();
-        ReducedHTMLParser.parse(originalResponse, l);
-
-        headerInsertPosition = l.getHeaderInsertPosition();
-        bodyInsertPosition = l.getBodyInsertPosition();
-        beforeBodyPosition = l.getBeforeBodyPosition();
-        afterBodyContentInsertPosition = l.getAfterBodyContentInsertPosition();
-        beforeBodyEndPosition = l.getAfterBodyEndPosition() - 7;  // 7, which is the length of </body>
-
-        parserCalled = true;
-    }
-
-    /**
-     * Writes the javascript code necessary for myfaces in every page, just befode the closing &lt;/body&gt; tag
-     */
-    public void writeMyFacesJavascriptBeforeBodyEnd(HttpServletRequest request,
-                                                    HttpServletResponse response) throws IOException
-    {
-        if (!parserCalled)
-        {
-            throw new IOException("Method parseResponse has to be called first");
-        }
-
-        if (beforeBodyEndPosition >= 0)
-        {
-            String myFacesJavascript = (String) request.getAttribute("org.apache.myfaces.myFacesJavascript");
-            if (myFacesJavascript != null)
-            {
-                originalResponse.insert(beforeBodyEndPosition, myFacesJavascript);
-            }
-            else
-            {
-                log.warn("MyFaces special javascript could not be retrieved from request-map.");
-            }
-        }
-    }
-
-    /**
-     * Add the resources to the &lt;head&gt; of the page.
-     * If the head tag is missing, but the &lt;body&gt; tag is present, the head tag is added.
-     * If both are missing, no resource is added.
-     * <p/>
-     * The ordering is such that the user header CSS & JS override the MyFaces' ones.
-     */
-    public void writeWithFullHeader(HttpServletRequest request,
-                                    HttpServletResponse response) throws IOException
-    {
-        if (!parserCalled)
-        {
-            throw new IOException("Method parseResponse has to be called first");
-        }
-
-        boolean addHeaderTags = false;
-
-        if (headerInsertPosition == -1)
-        {
-            if (beforeBodyPosition != -1)
-            {
-                // The input html has a body start tag, but no head tags. We therefore
-                // need to insert head start/end tags for our content to live in.
-                addHeaderTags = true;
-                headerInsertPosition = beforeBodyPosition;
-            }
-            else
-            {
-                // neither head nor body tags in the input
-                log.warn("Response has no <head> or <body> tag:\n" + originalResponse);
-            }
-        }
-
-        ResponseWriter writer = new org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl(response.getWriter(), org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils
-                .selectContentType(request.getHeader("accept")),
-                response.getCharacterEncoding());
-
-        if (afterBodyContentInsertPosition >= 0)
-        {
-            // insert all the items that want to go immediately after the <body> tag.
-            HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
-                    .getInstance(writer);
-
-            for (Iterator i = getBodyEndInfos().iterator(); i.hasNext();)
-            {
-                writerWrapper.write("\n");
-
-                PositionedInfo positionedInfo = (PositionedInfo) i.next();
-
-                if (!(positionedInfo instanceof WritablePositionedInfo))
-                    throw new IllegalStateException("positionedInfo of type : "
-                            + positionedInfo.getClass().getName());
-                ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
-                        writerWrapper);
-            }
-
-            originalResponse.insert(headerInsertPosition, writerWrapper.toString());
-        }
-
-        if (bodyInsertPosition > 0)
-        {
-            StringBuffer buf = new StringBuffer();
-            Set bodyInfos = getBodyOnloadInfos();
-            if (bodyInfos.size() > 0)
-            {
-                int i = 0;
-                for (Iterator it = getBodyOnloadInfos().iterator(); it.hasNext();)
-                {
-                    AttributeInfo positionedInfo = (AttributeInfo) it.next();
-                    if (i == 0)
-                    {
-                        buf.append(positionedInfo.getAttributeName());
-                        buf.append("=\"");
-                    }
-                    buf.append(positionedInfo.getAttributeValue());
-
-                    i++;
-                }
-
-                buf.append("\"");
-                originalResponse.insert(bodyInsertPosition - 1, " " + buf.toString());
-            }
-        }
-
-        if (headerInsertPosition >= 0)
-        {
-            HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
-                    .getInstance(writer);
-
-            if (addHeaderTags)
-                writerWrapper.write("<head>");
-
-            for (Iterator i = getHeaderBeginInfos().iterator(); i.hasNext();)
-            {
-                writerWrapper.write("\n");
-
-                PositionedInfo positionedInfo = (PositionedInfo) i.next();
-
-                if (!(positionedInfo instanceof WritablePositionedInfo))
-                    throw new IllegalStateException("positionedInfo of type : "
-                            + positionedInfo.getClass().getName());
-                ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
-                        writerWrapper);
-            }
-
-            if (addHeaderTags)
-                writerWrapper.write("</head>");
-
-            originalResponse.insert(headerInsertPosition, writerWrapper.toString());
-
-        }
-
-    }
-
-    /**
-     * Writes the response
-     */
-    public void writeResponse(HttpServletRequest request,
-                              HttpServletResponse response) throws IOException
-    {
-        ResponseWriter writer = new HtmlResponseWriterImpl(response.getWriter(), org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils
-                .selectContentType(request.getHeader("accept")),
-                response.getCharacterEncoding());
-        writer.write(originalResponse.toString());
-    }
-
-    private PositionedInfo getStyleInstance(FacesContext context, ResourceHandler resourceHandler)
-    {
-        return new StylePositionedInfo(getResourceUri(context, resourceHandler));
-    }
-
-    private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
-                                             boolean defer)
-    {
-        return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer);
-    }
-
-    private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
-            boolean defer, boolean encodeURL)
-    {
-   		return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer, encodeURL);
-    }
-    
-    private PositionedInfo getStyleInstance(FacesContext context, String uri)
-    {
-        return new StylePositionedInfo(getResourceUri(context, uri));
-    }
-
-    protected PositionedInfo getScriptInstance(FacesContext context, String uri, boolean defer)
-    {
-        return new ScriptPositionedInfo(getResourceUri(context, uri), defer);
-    }
-
-    private PositionedInfo getInlineScriptInstance(String inlineScript)
-    {
-        return new InlineScriptPositionedInfo(inlineScript);
-    }
-
-    private PositionedInfo getInlineStyleInstance(String inlineStyle)
-    {
-        return new InlineStylePositionedInfo(inlineStyle);
-    }
-
-    protected interface PositionedInfo
-    {
-    }
-
-    protected static class AttributeInfo implements PositionedInfo
-    {
-        private String _attributeName;
-        private String _attributeValue;
-
-        public String getAttributeName()
-        {
-            return _attributeName;
-        }
-
-        public void setAttributeName(String attributeName)
-        {
-            _attributeName = attributeName;
-        }
-
-        public String getAttributeValue()
-        {
-            return _attributeValue;
-        }
-
-        public void setAttributeValue(String attributeValue)
-        {
-            _attributeValue = attributeValue;
-        }
-    }
-
-    protected interface WritablePositionedInfo extends PositionedInfo
-    {
-        public abstract void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
-                throws IOException;
-    }
-
-    private abstract class AbstractResourceUri
-    {
-        protected final String _resourceUri;
-
-        protected AbstractResourceUri(String resourceUri)
-        {
-            _resourceUri = resourceUri;
-        }
-
-        public int hashCode()
-        {
-            return _resourceUri.hashCode();
-        }
-
-        public boolean equals(Object obj)
-        {
-            if (obj == null)
-            {
-                return false;
-            }
-            if (obj == this)
-            {
-                return true;
-            }
-            if (obj instanceof AbstractResourceUri)
-            {
-                AbstractResourceUri other = (AbstractResourceUri) obj;
-                return _resourceUri.equals(other._resourceUri);
-            }
-            return false;
-        }
-
-        protected String getResourceUri()
-        {
-            return _resourceUri;
-        }
-    }
-
-    private class StylePositionedInfo extends AbstractResourceUri implements WritablePositionedInfo
-    {
-        protected StylePositionedInfo(String resourceUri)
-        {
-            super(resourceUri);
-        }
-
-        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
-                throws IOException
-        {
-            writer.startElement(HTML.LINK_ELEM, null);
-            writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.REL_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.STYLESHEET_VALUE, null);
-            writer.writeAttribute(HTML.HREF_ATTR, response.encodeURL(this.getResourceUri()), null);
-            writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
-            writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.LINK_ELEM);
-        }
-    }
-
-    private class ScriptPositionedInfo extends AbstractResourceUri implements
-            WritablePositionedInfo
-    {
-        protected final boolean _defer;
-        protected final boolean _encode;
-
-        public ScriptPositionedInfo(String resourceUri, boolean defer)
-        {
-            this(resourceUri, defer, true);
-        }
-        
-        public ScriptPositionedInfo(String resourceUri, boolean defer, boolean encodeUrl)
-        {
-            super(resourceUri);
-            _defer = defer;
-            _encode = encodeUrl;
-        }
-
-        public int hashCode()
-        {
-            return new HashCodeBuilder()
-            	.append(this.getResourceUri())
-            	.append(_defer)
-            	.append(_encode)
-            	.toHashCode();
-        }
-
-        public boolean equals(Object obj)
-        {
-            if (super.equals(obj))
-            {
-                if (obj instanceof ScriptPositionedInfo)
-                {
-                    ScriptPositionedInfo other = (ScriptPositionedInfo) obj;
-                    return new EqualsBuilder()
-	                    .append(_defer, other._defer)
-	                    .append(_encode, other._encode)
-	                    .isEquals();
-                }
-            }
-            return false;
-        }
-
-        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
-                throws IOException
-        {
-            writer.startElement(HTML.SCRIPT_ELEM, null);
-            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-            if (_encode)
-            {
-            	writer.writeAttribute(HTML.SRC_ATTR, response.encodeURL(this.getResourceUri()), null);
-            }
-            else
-            {
-            	writer.writeAttribute(HTML.SRC_ATTR, this.getResourceUri(), null);
-            }
-
-            if (_defer)
-            {
-                writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM_DEFER_ATTR, "true", null);
-            }
-            writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM);
-        }
-    }
-
-    private abstract class InlinePositionedInfo implements WritablePositionedInfo
-    {
-        private final String _inlineValue;
-
-        protected InlinePositionedInfo(String inlineValue)
-        {
-            _inlineValue = inlineValue;
-        }
-
-        public String getInlineValue()
-        {
-            return _inlineValue;
-        }
-
-        public int hashCode()
-        {
-            return new HashCodeBuilder().append(_inlineValue).toHashCode();
-        }
-
-        public boolean equals(Object obj)
-        {
-            if (obj == null)
-            {
-                return false;
-            }
-            if (obj == this)
-            {
-                return true;
-            }
-            if (obj instanceof InlinePositionedInfo)
-            {
-                InlinePositionedInfo other = (InlinePositionedInfo) obj;
-                return new EqualsBuilder().append(_inlineValue, other._inlineValue).isEquals();
-            }
-            return false;
-        }
-    }
-
-    private class InlineScriptPositionedInfo extends InlinePositionedInfo
-    {
-        protected InlineScriptPositionedInfo(String inlineScript)
-        {
-            super(inlineScript);
-        }
-
-        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
-                throws IOException
-        {
-            writer.startElement(HTML.SCRIPT_ELEM, null);
-            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-            writer.writeText(getInlineValue(), null);
-            writer.endElement(HTML.SCRIPT_ELEM);
-        }
-    }
-
-    private class InlineStylePositionedInfo extends InlinePositionedInfo
-    {
-        protected InlineStylePositionedInfo(String inlineStyle)
-        {
-            super(inlineStyle);
-        }
-
-        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
-                throws IOException
-        {
-            writer.startElement(HTML.STYLE_ELEM, null);
-            writer.writeAttribute(HTML.REL_ATTR, HTML.STYLESHEET_VALUE, null);
-            writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
-            writer.writeText(getInlineValue(), null);
-            writer.endElement(HTML.STYLE_ELEM);
-        }
-    }
-
-    protected static class ParseCallbackListener implements CallbackListener
-    {
-        private int headerInsertPosition = -1;
-        private int bodyInsertPosition = -1;
-        private int beforeBodyPosition = -1;
-        private int afterBodyContentInsertPosition = -1;
-        private int afterBodyEndPosition = -1;
-
-        public ParseCallbackListener()
-        {
-        }
-
-        public void openedStartTag(int charIndex, int tagIdentifier)
-        {
-            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
-            {
-                beforeBodyPosition = charIndex;
-            }
-        }
-
-        public void closedStartTag(int charIndex, int tagIdentifier)
-        {
-            if (tagIdentifier == ReducedHTMLParser.HEAD_TAG)
-            {
-                headerInsertPosition = charIndex;
-            }
-            else if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
-            {
-                bodyInsertPosition = charIndex;
-            }
-        }
-
-        public void openedEndTag(int charIndex, int tagIdentifier)
-        {
-            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
-            {
-                afterBodyContentInsertPosition = charIndex;
-            }
-        }
-
-        public void closedEndTag(int charIndex, int tagIdentifier)
-        {
-            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
-            {
-                afterBodyEndPosition = charIndex;
-            }
-        }
-
-        public void attribute(int charIndex, int tagIdentifier, String key, String value)
-        {
-        }
-
-        public int getHeaderInsertPosition()
-        {
-            return headerInsertPosition;
-        }
-
-        public int getBodyInsertPosition()
-        {
-            return bodyInsertPosition;
-        }
-
-        public int getBeforeBodyPosition()
-        {
-            return beforeBodyPosition;
-        }
-
-        public int getAfterBodyContentInsertPosition()
-        {
-            return afterBodyContentInsertPosition;
-        }
-
-        public int getAfterBodyEndPosition()
-        {
-            return afterBodyEndPosition;
-        }
-    }
-
-    public boolean requiresBuffer()
-    {
-        return true;
-    }
-
-	public void responseStarted()
-	{
-	}
-	
-    public void responseFinished()
-    {
-    }
-}
+/*
+ * Copyright 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.myfaces.renderkit.html.util;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
+import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
+import org.apache.myfaces.shared_tomahawk.util.ClassUtils;
+
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a utility class to render link to resources used by custom components.
+ * Mostly used to avoid having to include [script src="..."][/script]
+ * in the head of the pages before using a component.
+ * <p/>
+ * When used together with the ExtensionsFilter, this class can allow components
+ * in the body of a page to emit script and stylesheet references into the page
+ * head section. The relevant methods on this object simply queue the changes,
+ * and when the page is complete the ExtensionsFilter calls back into this
+ * class to allow it to insert the commands into the buffered response.
+ * <p/>
+ * This class also works with the ExtensionsFilter to allow components to
+ * emit references to javascript/css/etc which are bundled in the component's
+ * jar file. Special URLs are generated which the ExtensionsFilter will later
+ * handle by retrieving the specified resource from the classpath.
+ * <p/>
+ * The special URL format is:
+ * <pre>
+ * {contextPath}/faces/myFacesExtensionResource/
+ *    {resourceLoaderName}/{cacheKey}/{resourceURI}
+ * </pre>
+ * Where:
+ * <ul>
+ * <li> {contextPath} is the context path of the current webapp
+ * <li> {resourceLoaderName} is the fully-qualified name of a class which
+ * implements the ResourceLoader interface. When a browser app sends a request
+ * for the specified resource, an instance of the specified ResourceLoader class
+ * will be created and passed the resourceURI part of the URL for resolving to the
+ * actual resource to be served back. The standard MyFaces ResourceLoader
+ * implementation only serves resources for files stored beneath path
+ * org/apache/myfaces/custom in the classpath but non-myfaces code can provide their
+ * own ResourceLoader implementations.
+ * </ul>
+ *
+ * @author Sylvain Vieujot (latest modification by $Author: mmarinschek $)
+ * @version $Revision: 371739 373827 $ $Date: 2006-01-31 14:50:35 +0000 (Tue, 31 Jan 2006) $
+ */
+public class DefaultAddResource implements AddResource
+{
+    private static final String PATH_SEPARATOR = "/";
+
+    protected static final Log log = LogFactory.getLog(DefaultAddResource.class);
+
+    private static final String RESOURCE_VIRTUAL_PATH = "/faces/myFacesExtensionResource";
+
+    private static final String RESOURCES_CACHE_KEY = AddResource.class.getName() + ".CACHE_KEY";
+
+    protected String _contextPath;
+
+    private StringBuffer originalResponse;
+
+    private Set headerBeginInfo;
+    private Set bodyEndInfo;
+    private Set bodyOnloadInfo;
+
+
+    protected boolean parserCalled = false;
+    protected int headerInsertPosition = -1;
+    protected int bodyInsertPosition = -1;
+    protected int beforeBodyPosition = -1;
+    protected int afterBodyContentInsertPosition = -1;
+    protected int beforeBodyEndPosition = -1;
+
+    protected DefaultAddResource()
+    {
+    }
+
+    /**
+     * the context path for the web-app.<br />
+     * You can set the context path only once, every subsequent set will throw an SecurityException
+     */
+    public void setContextPath(String contextPath)
+    {
+        if (_contextPath != null)
+        {
+            throw new SecurityException("context path already set");
+        }
+
+        _contextPath = contextPath;
+    }
+
+    // Methods to add resources
+
+    /**
+     * Insert a [script src="url"] entry at the current location in the response.
+     * The resource is expected to be in the classpath, at the same location as the
+     * specified component + "/resource".
+     * <p/>
+     * Example: when customComponent is class example.Widget, and
+     * resourceName is script.js, the resource will be retrieved from
+     * "example/Widget/resource/script.js" in the classpath.
+     */
+    public void addJavaScriptHere(FacesContext context, Class myfacesCustomComponent,
+                                  String resourceName) throws IOException
+    {
+        addJavaScriptHere(context, new MyFacesResourceHandler(myfacesCustomComponent, resourceName));
+    }
+
+    /**
+     * Insert a [script src="url"] entry at the current location in the response.
+     *
+     * @param uri is the location of the desired resource, relative to the base
+     *            directory of the webapp (ie its contextPath).
+     */
+    public void addJavaScriptHere(FacesContext context, String uri) throws IOException
+    {
+        ResponseWriter writer = context.getResponseWriter();
+
+        writer.startElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM, null);
+        writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+        String src = context.getExternalContext().encodeResourceURL(getResourceUri(context, uri));
+        writer.writeURIAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SRC_ATTR, src, null);
+        writer.endElement(HTML.SCRIPT_ELEM);
+    }
+
+	public void addJavaScriptHerePlain(FacesContext context, String uri) throws IOException
+	{
+        ResponseWriter writer = context.getResponseWriter();
+
+        writer.startElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM, null);
+        writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+        String src = getResourceUri(context, uri);
+        writer.writeURIAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SRC_ATTR, src, null);
+        writer.endElement(HTML.SCRIPT_ELEM);
+	}
+	
+    /**
+     * Insert a [script src="url"] entry at the current location in the response.
+     *
+     * @param context
+     * @param resourceHandler is an object which specifies exactly how to build the url
+     *                        that is emitted into the script tag. Code which needs to generate URLs in ways
+     *                        that this class does not support by default can implement a custom ResourceHandler.
+     * @throws IOException
+     */
+    public void addJavaScriptHere(FacesContext context, ResourceHandler resourceHandler)
+            throws IOException
+    {
+        validateResourceHandler(resourceHandler);
+
+        ResponseWriter writer = context.getResponseWriter();
+
+        writer.startElement(HTML.SCRIPT_ELEM, null);
+        writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+        String src = context.getExternalContext().encodeResourceURL(
+                getResourceUri(context, resourceHandler));
+        writer.writeURIAttribute(HTML.SRC_ATTR, src, null);
+        writer.endElement(HTML.SCRIPT_ELEM);
+    }
+
+    public void addResourceHere(FacesContext context, ResourceHandler resourceHandler)
+            throws IOException
+    {
+        validateResourceHandler(resourceHandler);
+
+        String path = getResourceUri(context, resourceHandler);
+        ResponseWriter writer = context.getResponseWriter();
+        writer.write(context.getExternalContext().encodeResourceURL(path));
+    }
+
+    /**
+     * Verify that the resource handler is acceptable. Null is not
+     * valid, and the getResourceLoaderClass method must return a
+     * Class object whose instances implements the ResourceLoader
+     * interface.
+     *
+     * @param resourceHandler
+     */
+    protected void validateResourceHandler(ResourceHandler resourceHandler)
+    {
+        if (resourceHandler == null)
+        {
+            throw new IllegalArgumentException("ResourceHandler is null");
+        }
+        validateResourceLoader(resourceHandler.getResourceLoaderClass());
+    }
+
+    /**
+     * Given a Class object, verify that the instances of that class
+     * implement the ResourceLoader interface.
+     *
+     * @param resourceloader
+     */
+    protected void validateResourceLoader(Class resourceloader)
+    {
+        if (!ResourceLoader.class.isAssignableFrom(resourceloader))
+        {
+            throw new FacesException("Class " + resourceloader.getName() + " must implement "
+                    + ResourceLoader.class.getName());
+        }
+    }
+
+    /**
+     * Adds the given Javascript resource to the document header at the specified
+     * document positioy by supplying a resourcehandler instance.
+     * <p/>
+     * Use this method to have full control about building the reference url
+     * to identify the resource and to customize how the resource is
+     * written to the response. In most cases, however, one of the convenience
+     * methods on this class can be used without requiring a custom ResourceHandler
+     * to be provided.
+     * <p/>
+     * If the script has already been referenced, it's added only once.
+     * <p/>
+     * Note that this method <i>queues</i> the javascript for insertion, and that
+     * the script is inserted into the buffered response by the ExtensionsFilter
+     * after the page is complete.
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
+                                        ResourceHandler resourceHandler)
+    {
+        addJavaScriptAtPosition(context, position, resourceHandler, false);
+    }
+
+    /**
+     * Insert a [script src="url"] entry into the document header at the
+     * specified document position. If the script has already been
+     * referenced, it's added only once.
+     * <p/>
+     * The resource is expected to be in the classpath, at the same location as the
+     * specified component + "/resource".
+     * <p/>
+     * Example: when customComponent is class example.Widget, and
+     * resourceName is script.js, the resource will be retrieved from
+     * "example/Widget/resource/script.js" in the classpath.
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
+                                        Class myfacesCustomComponent, String resourceName)
+    {
+        addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
+                myfacesCustomComponent, resourceName));
+    }
+    
+	public void addJavaScriptAtPositionPlain(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
+	{
+        addJavaScriptAtPosition(context, position,
+        		new MyFacesResourceHandler(myfacesCustomComponent, resourceName),
+        		false, false);
+	}
+	
+
+    /**
+     * Insert a [script src="url"] entry into the document header at the
+     * specified document position. If the script has already been
+     * referenced, it's added only once.
+     *
+     * @param defer specifies whether the html attribute "defer" is set on the
+     *              generated script tag. If this is true then the browser will continue
+     *              processing the html page without waiting for the specified script to
+     *              load and be run.
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
+                                        Class myfacesCustomComponent, String resourceName, boolean defer)
+    {
+        addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
+                myfacesCustomComponent, resourceName), defer);
+    }
+
+    /**
+     * Insert a [script src="url"] entry into the document header at the
+     * specified document position. If the script has already been
+     * referenced, it's added only once.
+     *
+     * @param uri is the location of the desired resource, relative to the base
+     *            directory of the webapp (ie its contextPath).
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri)
+    {
+        addJavaScriptAtPosition(context, position, uri, false);
+    }
+
+    /**
+     * Adds the given Javascript resource at the specified document position.
+     * If the script has already been referenced, it's added only once.
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri,
+                                        boolean defer)
+    {
+        addPositionedInfo(position, getScriptInstance(context, uri, defer));
+    }
+
+    public void addJavaScriptToBodyTag(FacesContext context, String javascriptEventName,
+                                       String addedJavaScript)
+    {
+        AttributeInfo info = new AttributeInfo();
+        info.setAttributeName(javascriptEventName);
+        info.setAttributeValue(addedJavaScript);
+
+        addPositionedInfo(BODY_ONLOAD, info);
+    }
+
+    /**
+     * Adds the given Javascript resource at the specified document position.
+     * If the script has already been referenced, it's added only once.
+     */
+    public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
+                                        ResourceHandler resourceHandler, boolean defer)
+    {
+        validateResourceHandler(resourceHandler);
+        addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer));
+    }
+
+    private void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
+            ResourceHandler resourceHandler, boolean defer, boolean encodeUrl)
+	{
+		validateResourceHandler(resourceHandler);
+		addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer, encodeUrl));
+	}
+    
+    /**
+     * Adds the given Style Sheet at the specified document position.
+     * If the style sheet has already been referenced, it's added only once.
+     */
+    public void addStyleSheet(FacesContext context, ResourcePosition position,
+                              Class myfacesCustomComponent, String resourceName)
+    {
+        addStyleSheet(context, position, new MyFacesResourceHandler(myfacesCustomComponent,
+                resourceName));
+    }
+
+    /**
+     * Adds the given Style Sheet at the specified document position.
+     * If the style sheet has already been referenced, it's added only once.
+     */
+    public void addStyleSheet(FacesContext context, ResourcePosition position, String uri)
+    {
+        addPositionedInfo(position, getStyleInstance(context, uri));
+    }
+
+    /**
+     * Adds the given Style Sheet at the specified document position.
+     * If the style sheet has already been referenced, it's added only once.
+     */
+    public void addStyleSheet(FacesContext context, ResourcePosition position,
+                              ResourceHandler resourceHandler)
+    {
+        validateResourceHandler(resourceHandler);
+        addPositionedInfo(position, getStyleInstance(context, resourceHandler));
+    }
+
+    /**
+     * Adds the given Inline Style at the specified document position.
+     */
+    public void addInlineStyleAtPosition(FacesContext context, ResourcePosition position, String inlineStyle)
+    {
+        addPositionedInfo(position, getInlineStyleInstance(inlineStyle));
+    }
+
+    /**
+     * Adds the given Inline Script at the specified document position.
+     */
+    public void addInlineScriptAtPosition(FacesContext context, ResourcePosition position,
+                                          String inlineScript)
+    {
+        addPositionedInfo(position, getInlineScriptInstance(inlineScript));
+    }
+
+    public String getResourceUri(FacesContext context, Class myfacesCustomComponent,
+                                 String resource, boolean withContextPath)
+    {
+        return getResourceUri(context,
+                new MyFacesResourceHandler(myfacesCustomComponent, resource), withContextPath);
+    }
+
+    public String getResourceUri(FacesContext context, Class myfacesCustomComponent, String resource)
+    {
+        return getResourceUri(context, new MyFacesResourceHandler(myfacesCustomComponent, resource));
+    }
+
+    /**
+     * Get the Path used to retrieve an resource.
+     */
+    public String getResourceUri(FacesContext context, ResourceHandler resourceHandler)
+    {
+        String uri = resourceHandler.getResourceUri(context);
+        if (uri == null)
+        {
+            return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true);
+        }
+        return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true) + uri;
+    }
+
+    /**
+     * Get the Path used to retrieve an resource.
+     */
+    public String getResourceUri(FacesContext context, ResourceHandler resourceHandler,
+                                 boolean withContextPath)
+    {
+        String uri = resourceHandler.getResourceUri(context);
+        if (uri == null)
+        {
+            return getResourceUri(context, resourceHandler.getResourceLoaderClass(),
+                    withContextPath);
+        }
+        return getResourceUri(context, resourceHandler.getResourceLoaderClass(), withContextPath)
+                + uri;
+    }
+
+    /**
+     * Get the Path used to retrieve an resource.
+     */
+    public String getResourceUri(FacesContext context, String uri)
+    {
+        return getResourceUri(context, uri, true);
+    }
+
+    /**
+     * Get the Path used to retrieve an resource.
+     */
+    public String getResourceUri(FacesContext context, String uri, boolean withContextPath)
+    {
+        if (withContextPath)
+        {
+            return context.getApplication().getViewHandler().getResourceURL(context, uri);
+        }
+        return uri;
+    }
+
+    /**
+     * Get the Path used to retrieve an resource.
+     */
+    protected String getResourceUri(FacesContext context, Class resourceLoader,
+                                    boolean withContextPath)
+    {
+        StringBuffer sb = new StringBuffer(200);
+        sb.append(RESOURCE_VIRTUAL_PATH);
+        sb.append(PATH_SEPARATOR);
+        sb.append(resourceLoader.getName());
+        sb.append(PATH_SEPARATOR);
+        sb.append(getCacheKey(context));
+        sb.append(PATH_SEPARATOR);
+        return getResourceUri(context, sb.toString(), withContextPath);
+    }
+
+    /**
+     * Return a value used in the {cacheKey} part of a generated URL for a
+     * resource reference.
+     * <p/>
+     * Caching in browsers normally works by having files served to them
+     * include last-modified and expiry-time http headers. Until the expiry
+     * time is reached, a browser will silently use its cached version. After
+     * the expiry time, it will send a "get if modified since {time}" message,
+     * where {time} is the last-modified header from the version it has cached.
+     * <p/>
+     * Unfortunately this scheme only works well for resources represented as
+     * plain files on disk, where the webserver can easily and efficiently see
+     * the last-modified time of the resource file. When that query has to be
+     * processed by a servlet that doesn't scale well, even when it is possible
+     * to determine the resource's last-modified date from servlet code.
+     * <p/>
+     * Fortunately, for the AddResource class a static resource is only ever
+     * accessed because a URL was embedded by this class in a dynamic page.
+     * This makes it possible to implement caching by instead marking every
+     * resource served with a very long expiry time, but forcing the URL that
+     * points to the resource to change whenever the old cached version becomes
+     * invalid; the browser effectively thinks it is fetching a different
+     * resource that it hasn't seen before. This is implemented by embedding
+     * a "cache key" in the generated URL.
+     * <p/>
+     * Rather than using the actual modification date of a resource as the
+     * cache key, we simply use the webapp deployment time. This means that all
+     * data cached by browsers will become invalid after a webapp deploy (all
+     * the urls to the resources change). It also means that changes that occur
+     * to a resource <i>without</i> a webapp redeploy will not be seen by browsers.
+     */
+    protected long getCacheKey(FacesContext context)
+    {
+        // cache key is hold in application scope so it is recreated on redeploying the webapp.
+        Map applicationMap = context.getExternalContext().getApplicationMap();
+        Long cacheKey = (Long) applicationMap.get(RESOURCES_CACHE_KEY);
+        if (cacheKey == null)
+        {
+            cacheKey = new Long(System.currentTimeMillis() / 100000);
+            applicationMap.put(RESOURCES_CACHE_KEY, cacheKey);
+        }
+        return cacheKey.longValue();
+    }
+
+    public boolean isResourceUri(HttpServletRequest request)
+    {
+        String path;
+        if (_contextPath != null)
+        {
+            path = _contextPath + RESOURCE_VIRTUAL_PATH;
+        }
+        else
+        {
+            path = RESOURCE_VIRTUAL_PATH;
+        }
+        return request.getRequestURI().startsWith(path);
+    }
+
+    private Class getClass(String className) throws ClassNotFoundException
+    {
+        Class clazz = ClassUtils.classForName(className);
+        validateResourceLoader(clazz);
+        return clazz;
+    }
+
+    public void serveResource(ServletContext context, HttpServletRequest request,
+                              HttpServletResponse response) throws IOException
+    {
+        String pathInfo = request.getPathInfo();
+        String uri = request.getContextPath() + request.getServletPath()
+                + (pathInfo == null ? "" : pathInfo);
+        String classNameStartsAfter = RESOURCE_VIRTUAL_PATH + '/';
+
+        int posStartClassName = uri.indexOf(classNameStartsAfter) + classNameStartsAfter.length();
+        int posEndClassName = uri.indexOf(PATH_SEPARATOR, posStartClassName);
+        String className = uri.substring(posStartClassName, posEndClassName);
+        int posEndCacheKey = uri.indexOf(PATH_SEPARATOR, posEndClassName + 1);
+        String resourceUri = null;
+        if (posEndCacheKey + 1 < uri.length())
+        {
+            resourceUri = uri.substring(posEndCacheKey + 1);
+        }
+        try
+        {
+            Class resourceLoader = getClass(className);
+            validateResourceLoader(resourceLoader);
+            ((ResourceLoader) resourceLoader.newInstance()).serveResource(context, request,
+                    response, resourceUri);
+            response.flushBuffer();
+        }
+        catch (ClassNotFoundException e)
+        {
+            log.error("Could not find class for name: " + className, e);
+            response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                    "Could not find resourceloader class for name: " + className);
+        }
+        catch (InstantiationException e)
+        {
+            log.error("Could not instantiate class for name: " + className, e);
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                    "Could not instantiate resourceloader class for name: " + className);
+        }
+        catch (IllegalAccessException e)
+        {
+            log.error("Could not access class for name: " + className, e);
+            response.sendError(HttpServletResponse.SC_FORBIDDEN,
+                    "Could not access resourceloader class for name: " + className);
+        }
+        catch (Throwable e)
+        {
+            log.error("Error while serving resource: " + resourceUri + ", message : " + e.getMessage(), e);
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    // Positioned stuffs
+
+    protected Set getHeaderBeginInfos()
+    {
+        if (headerBeginInfo == null)
+        {
+            headerBeginInfo = new LinkedHashSet();
+        }
+        return headerBeginInfo;
+    }
+
+    protected Set getBodyEndInfos()
+    {
+        if (bodyEndInfo == null)
+        {
+            bodyEndInfo = new LinkedHashSet();
+        }
+        return bodyEndInfo;
+    }
+
+    protected Set getBodyOnloadInfos()
+    {
+        if (bodyOnloadInfo == null)
+        {
+            bodyOnloadInfo = new LinkedHashSet();
+        }
+        return bodyOnloadInfo;
+    }
+
+    private void addPositionedInfo(ResourcePosition position, PositionedInfo info)
+    {
+        if (HEADER_BEGIN.equals(position))
+        {
+            Set set = getHeaderBeginInfos();
+            set.add(info);
+        }
+        else if (BODY_END.equals(position))
+        {
+            Set set = getBodyEndInfos();
+            set.add(info);
+
+        }
+        else if (BODY_ONLOAD.equals(position))
+        {
+            Set set = getBodyOnloadInfos();
+            set.add(info);
+        }
+    }
+
+    public boolean hasHeaderBeginInfos()
+    {
+        return headerBeginInfo != null;
+    }
+
+    /**
+     * Parses the response to mark the positions where code will be inserted
+     */
+    public void parseResponse(HttpServletRequest request, String bufferedResponse,
+                              HttpServletResponse response)
+    {
+
+        originalResponse = new StringBuffer(bufferedResponse);
+
+        ParseCallbackListener l = new ParseCallbackListener();
+        ReducedHTMLParser.parse(originalResponse, l);
+
+        headerInsertPosition = l.getHeaderInsertPosition();
+        bodyInsertPosition = l.getBodyInsertPosition();
+        beforeBodyPosition = l.getBeforeBodyPosition();
+        afterBodyContentInsertPosition = l.getAfterBodyContentInsertPosition();
+        beforeBodyEndPosition = l.getAfterBodyEndPosition() - 7;  // 7, which is the length of </body>
+
+        parserCalled = true;
+    }
+
+    /**
+     * Writes the javascript code necessary for myfaces in every page, just befode the closing &lt;/body&gt; tag
+     */
+    public void writeMyFacesJavascriptBeforeBodyEnd(HttpServletRequest request,
+                                                    HttpServletResponse response) throws IOException
+    {
+        if (!parserCalled)
+        {
+            throw new IOException("Method parseResponse has to be called first");
+        }
+
+        if (beforeBodyEndPosition >= 0)
+        {
+            String myFacesJavascript = (String) request.getAttribute("org.apache.myfaces.myFacesJavascript");
+            if (myFacesJavascript != null)
+            {
+                originalResponse.insert(beforeBodyEndPosition, myFacesJavascript);
+            }
+            else
+            {
+                log.warn("MyFaces special javascript could not be retrieved from request-map.");
+            }
+        }
+    }
+
+    /**
+     * Add the resources to the &lt;head&gt; of the page.
+     * If the head tag is missing, but the &lt;body&gt; tag is present, the head tag is added.
+     * If both are missing, no resource is added.
+     * <p/>
+     * The ordering is such that the user header CSS & JS override the MyFaces' ones.
+     */
+    public void writeWithFullHeader(HttpServletRequest request,
+                                    HttpServletResponse response) throws IOException
+    {
+        if (!parserCalled)
+        {
+            throw new IOException("Method parseResponse has to be called first");
+        }
+
+        boolean addHeaderTags = false;
+
+        if (headerInsertPosition == -1)
+        {
+            if (beforeBodyPosition != -1)
+            {
+                // The input html has a body start tag, but no head tags. We therefore
+                // need to insert head start/end tags for our content to live in.
+                addHeaderTags = true;
+                headerInsertPosition = beforeBodyPosition;
+            }
+            else
+            {
+                // neither head nor body tags in the input
+                log.warn("Response has no <head> or <body> tag:\n" + originalResponse);
+            }
+        }
+
+        ResponseWriter writer = new org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl(response.getWriter(), org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils
+                .selectContentType(request.getHeader("accept")),
+                response.getCharacterEncoding());
+
+        if (afterBodyContentInsertPosition >= 0)
+        {
+            // insert all the items that want to go immediately after the <body> tag.
+            HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
+                    .getInstance(writer);
+
+            for (Iterator i = getBodyEndInfos().iterator(); i.hasNext();)
+            {
+                writerWrapper.write("\n");
+
+                PositionedInfo positionedInfo = (PositionedInfo) i.next();
+
+                if (!(positionedInfo instanceof WritablePositionedInfo))
+                    throw new IllegalStateException("positionedInfo of type : "
+                            + positionedInfo.getClass().getName());
+                ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
+                        writerWrapper);
+            }
+
+            originalResponse.insert(headerInsertPosition, writerWrapper.toString());
+        }
+
+        if (bodyInsertPosition > 0)
+        {
+            StringBuffer buf = new StringBuffer();
+            Set bodyInfos = getBodyOnloadInfos();
+            if (bodyInfos.size() > 0)
+            {
+                int i = 0;
+                for (Iterator it = getBodyOnloadInfos().iterator(); it.hasNext();)
+                {
+                    AttributeInfo positionedInfo = (AttributeInfo) it.next();
+                    if (i == 0)
+                    {
+                        buf.append(positionedInfo.getAttributeName());
+                        buf.append("=\"");
+                    }
+                    buf.append(positionedInfo.getAttributeValue());
+
+                    i++;
+                }
+
+                buf.append("\"");
+                originalResponse.insert(bodyInsertPosition - 1, " " + buf.toString());
+            }
+        }
+
+        if (headerInsertPosition >= 0)
+        {
+            HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
+                    .getInstance(writer);
+
+            if (addHeaderTags)
+                writerWrapper.write("<head>");
+
+            for (Iterator i = getHeaderBeginInfos().iterator(); i.hasNext();)
+            {
+                writerWrapper.write("\n");
+
+                PositionedInfo positionedInfo = (PositionedInfo) i.next();
+
+                if (!(positionedInfo instanceof WritablePositionedInfo))
+                    throw new IllegalStateException("positionedInfo of type : "
+                            + positionedInfo.getClass().getName());
+                ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
+                        writerWrapper);
+            }
+
+            if (addHeaderTags)
+                writerWrapper.write("</head>");
+
+            originalResponse.insert(headerInsertPosition, writerWrapper.toString());
+
+        }
+
+    }
+
+    /**
+     * Writes the response
+     */
+    public void writeResponse(HttpServletRequest request,
+                              HttpServletResponse response) throws IOException
+    {
+        ResponseWriter writer = new HtmlResponseWriterImpl(response.getWriter(), org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils
+                .selectContentType(request.getHeader("accept")),
+                response.getCharacterEncoding());
+        writer.write(originalResponse.toString());
+    }
+
+    private PositionedInfo getStyleInstance(FacesContext context, ResourceHandler resourceHandler)
+    {
+        return new StylePositionedInfo(getResourceUri(context, resourceHandler));
+    }
+
+    private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
+                                             boolean defer)
+    {
+        return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer);
+    }
+
+    private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
+            boolean defer, boolean encodeURL)
+    {
+   		return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer, encodeURL);
+    }
+    
+    private PositionedInfo getStyleInstance(FacesContext context, String uri)
+    {
+        return new StylePositionedInfo(getResourceUri(context, uri));
+    }
+
+    protected PositionedInfo getScriptInstance(FacesContext context, String uri, boolean defer)
+    {
+        return new ScriptPositionedInfo(getResourceUri(context, uri), defer);
+    }
+
+    private PositionedInfo getInlineScriptInstance(String inlineScript)
+    {
+        return new InlineScriptPositionedInfo(inlineScript);
+    }
+
+    private PositionedInfo getInlineStyleInstance(String inlineStyle)
+    {
+        return new InlineStylePositionedInfo(inlineStyle);
+    }
+
+    protected interface PositionedInfo
+    {
+    }
+
+    protected static class AttributeInfo implements PositionedInfo
+    {
+        private String _attributeName;
+        private String _attributeValue;
+
+        public String getAttributeName()
+        {
+            return _attributeName;
+        }
+
+        public void setAttributeName(String attributeName)
+        {
+            _attributeName = attributeName;
+        }
+
+        public String getAttributeValue()
+        {
+            return _attributeValue;
+        }
+
+        public void setAttributeValue(String attributeValue)
+        {
+            _attributeValue = attributeValue;
+        }
+    }
+
+    protected interface WritablePositionedInfo extends PositionedInfo
+    {
+        public abstract void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
+                throws IOException;
+    }
+
+    private abstract class AbstractResourceUri
+    {
+        protected final String _resourceUri;
+
+        protected AbstractResourceUri(String resourceUri)
+        {
+            _resourceUri = resourceUri;
+        }
+
+        public int hashCode()
+        {
+            return _resourceUri.hashCode();
+        }
+
+        public boolean equals(Object obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+            if (obj == this)
+            {
+                return true;
+            }
+            if (obj instanceof AbstractResourceUri)
+            {
+                AbstractResourceUri other = (AbstractResourceUri) obj;
+                return _resourceUri.equals(other._resourceUri);
+            }
+            return false;
+        }
+
+        protected String getResourceUri()
+        {
+            return _resourceUri;
+        }
+    }
+
+    private class StylePositionedInfo extends AbstractResourceUri implements WritablePositionedInfo
+    {
+        protected StylePositionedInfo(String resourceUri)
+        {
+            super(resourceUri);
+        }
+
+        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
+                throws IOException
+        {
+            writer.startElement(HTML.LINK_ELEM, null);
+            writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.REL_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.STYLESHEET_VALUE, null);
+            writer.writeAttribute(HTML.HREF_ATTR, response.encodeURL(this.getResourceUri()), null);
+            writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
+            writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.LINK_ELEM);
+        }
+    }
+
+    private class ScriptPositionedInfo extends AbstractResourceUri implements
+            WritablePositionedInfo
+    {
+        protected final boolean _defer;
+        protected final boolean _encode;
+
+        public ScriptPositionedInfo(String resourceUri, boolean defer)
+        {
+            this(resourceUri, defer, true);
+        }
+        
+        public ScriptPositionedInfo(String resourceUri, boolean defer, boolean encodeUrl)
+        {
+            super(resourceUri);
+            _defer = defer;
+            _encode = encodeUrl;
+        }
+
+        public int hashCode()
+        {
+            return new HashCodeBuilder()
+            	.append(this.getResourceUri())
+            	.append(_defer)
+            	.append(_encode)
+            	.toHashCode();
+        }
+
+        public boolean equals(Object obj)
+        {
+            if (super.equals(obj))
+            {
+                if (obj instanceof ScriptPositionedInfo)
+                {
+                    ScriptPositionedInfo other = (ScriptPositionedInfo) obj;
+                    return new EqualsBuilder()
+	                    .append(_defer, other._defer)
+	                    .append(_encode, other._encode)
+	                    .isEquals();
+                }
+            }
+            return false;
+        }
+
+        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
+                throws IOException
+        {
+            writer.startElement(HTML.SCRIPT_ELEM, null);
+            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+            if (_encode)
+            {
+            	writer.writeAttribute(HTML.SRC_ATTR, response.encodeURL(this.getResourceUri()), null);
+            }
+            else
+            {
+            	writer.writeAttribute(HTML.SRC_ATTR, this.getResourceUri(), null);
+            }
+
+            if (_defer)
+            {
+                writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM_DEFER_ATTR, "true", null);
+            }
+            writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM);
+        }
+    }
+
+    private abstract class InlinePositionedInfo implements WritablePositionedInfo
+    {
+        private final String _inlineValue;
+
+        protected InlinePositionedInfo(String inlineValue)
+        {
+            _inlineValue = inlineValue;
+        }
+
+        public String getInlineValue()
+        {
+            return _inlineValue;
+        }
+
+        public int hashCode()
+        {
+            return new HashCodeBuilder().append(_inlineValue).toHashCode();
+        }
+
+        public boolean equals(Object obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+            if (obj == this)
+            {
+                return true;
+            }
+            if (obj instanceof InlinePositionedInfo)
+            {
+                InlinePositionedInfo other = (InlinePositionedInfo) obj;
+                return new EqualsBuilder().append(_inlineValue, other._inlineValue).isEquals();
+            }
+            return false;
+        }
+    }
+
+    private class InlineScriptPositionedInfo extends InlinePositionedInfo
+    {
+        protected InlineScriptPositionedInfo(String inlineScript)
+        {
+            super(inlineScript);
+        }
+
+        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
+                throws IOException
+        {
+            writer.startElement(HTML.SCRIPT_ELEM, null);
+            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+            writer.writeText(getInlineValue(), null);
+            writer.endElement(HTML.SCRIPT_ELEM);
+        }
+    }
+
+    private class InlineStylePositionedInfo extends InlinePositionedInfo
+    {
+        protected InlineStylePositionedInfo(String inlineStyle)
+        {
+            super(inlineStyle);
+        }
+
+        public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
+                throws IOException
+        {
+            writer.startElement(HTML.STYLE_ELEM, null);
+            writer.writeAttribute(HTML.REL_ATTR, HTML.STYLESHEET_VALUE, null);
+            writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
+            writer.writeText(getInlineValue(), null);
+            writer.endElement(HTML.STYLE_ELEM);
+        }
+    }
+
+    protected static class ParseCallbackListener implements CallbackListener
+    {
+        private int headerInsertPosition = -1;
+        private int bodyInsertPosition = -1;
+        private int beforeBodyPosition = -1;
+        private int afterBodyContentInsertPosition = -1;
+        private int afterBodyEndPosition = -1;
+
+        public ParseCallbackListener()
+        {
+        }
+
+        public void openedStartTag(int charIndex, int tagIdentifier)
+        {
+            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
+            {
+                beforeBodyPosition = charIndex;
+            }
+        }
+
+        public void closedStartTag(int charIndex, int tagIdentifier)
+        {
+            if (tagIdentifier == ReducedHTMLParser.HEAD_TAG)
+            {
+                headerInsertPosition = charIndex;
+            }
+            else if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
+            {
+                bodyInsertPosition = charIndex;
+            }
+        }
+
+        public void openedEndTag(int charIndex, int tagIdentifier)
+        {
+            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
+            {
+                afterBodyContentInsertPosition = charIndex;
+            }
+        }
+
+        public void closedEndTag(int charIndex, int tagIdentifier)
+        {
+            if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
+            {
+                afterBodyEndPosition = charIndex;
+            }
+        }
+
+        public void attribute(int charIndex, int tagIdentifier, String key, String value)
+        {
+        }
+
+        public int getHeaderInsertPosition()
+        {
+            return headerInsertPosition;
+        }
+
+        public int getBodyInsertPosition()
+        {
+            return bodyInsertPosition;
+        }
+
+        public int getBeforeBodyPosition()
+        {
+            return beforeBodyPosition;
+        }
+

[... 25 lines stripped ...]