Java tutorial
/* * 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.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.config.MyfacesConfig; import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML; import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils; import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl; import org.apache.myfaces.shared_tomahawk.util.ClassUtils; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; 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.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 extends NonBufferingAddResource { protected Log log = LogFactory.getLog(DefaultAddResource.class); protected 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() { } // Methods to add resources /** * 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)); } // 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 </body> 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 <head> of the page. * If the head tag is missing, but the <body> 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 HtmlResponseWriterImpl(response.getWriter(), 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(), 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 { writeStyleReference(response, writer, this.getResourceUri()); } } 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 { writeJavaScriptReference(response, writer, this.getResourceUri(), _encode, _defer); } } 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 { writeInlineScript(writer, getInlineValue()); } } private class InlineStylePositionedInfo extends InlinePositionedInfo { protected InlineStylePositionedInfo(String inlineStyle) { super(inlineStyle); } public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer) throws IOException { writeInlineStylesheet(writer, getInlineValue()); } } 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() { } }