ch.entwine.weblounge.taglib.WebloungeTag.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.taglib.WebloungeTag.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.taglib;

import ch.entwine.weblounge.common.impl.request.RequestUtils;
import ch.entwine.weblounge.common.impl.request.WebloungeRequestImpl;
import ch.entwine.weblounge.common.impl.request.WebloungeResponseImpl;
import ch.entwine.weblounge.common.request.WebloungeRequest;
import ch.entwine.weblounge.common.request.WebloungeResponse;
import ch.entwine.weblounge.common.site.Environment;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TryCatchFinally;

/**
 * Base class for weblounge tags which implements most of the standard
 * <code>HTML 4</code> tag attributes.
 */
public class WebloungeTag extends BodyTagSupport implements TryCatchFinally {

    /** Serial version id */
    private static final long serialVersionUID = 1754816467985401658L;

    /** The logging facility */
    private static final Logger logger = LoggerFactory.getLogger(WebloungeTag.class);

    /** Css class attribute */
    protected String css = null;

    /** Css style attribute */
    protected String style = null;

    /** lang attribute */
    protected String lang = null;

    /** dir (reading direction) attribute */
    protected String dir = null;

    /** Html title */
    protected String title = null;

    /** Html name */
    protected String name = null;

    /** OnClick Event */
    protected String onclick = null;

    /** OnDblClick Event */
    protected String ondblclick = null;

    /** OnMouseDown Event */
    protected String onmousedown = null;

    /** OnMouseUp Event */
    protected String onmouseup = null;

    /** OnMouseMove Event */
    protected String onmousemove = null;

    /** OnMouseOver Event */
    protected String onmouseover = null;

    /** OnMouseOut Event */
    protected String onmouseout = null;

    /** OnKeyDown Event */
    protected String onkeydown = null;

    /** OnKeyPress Event */
    protected String onkeypress = null;

    /** OnKeyUp Event */
    protected String onkeyup = null;

    /** The weblounge request */
    protected WebloungeRequest request = null;

    /** The weblounge response */
    protected WebloungeResponse response = null;

    /** The stash */
    protected Map<String, Object> stash = new HashMap<String, Object>();

    /** The attributes that were added by this tag instance */
    protected List<String> attributes = new ArrayList<String>();

    /**
     * Resets the properties of this tag to default values. This method is called
     * between <strong>every</strong> request.
     * <p>
     * If you override this method make sure you call <code>super.reset()</code>
     */
    protected void reset() {
        css = null;
        style = null;
        lang = null;
        dir = null;
        title = null;
        name = null;
        onclick = null;
        ondblclick = null;
        onmousedown = null;
        onmouseup = null;
        onmousemove = null;
        onmouseover = null;
        onmouseout = null;
        onkeydown = null;
        onkeypress = null;
        onkeyup = null;
        request = null;
        response = null;
        stash.clear();
        attributes.clear();
    }

    /**
     * Sets the standard <code>HTML</code> <code>class</code> attribute.
     * 
     * @param c
     *          the css class
     */
    public void setClass(String c) {
        css = c;
    }

    /**
     * Sets the standard <code>HTML</code> <code>class</code> attribute.
     * 
     * @param c
     *          the css class
     */
    public void setCss(String c) {
        css = c;
    }

    /**
     * Adds the class to the css class attribute.
     * 
     * @param c
     *          the class name
     */
    protected void addCssClass(String c) {
        if (StringUtils.trimToNull(c) == null)
            return;
        if (css == null)
            css = c;
        else if (!css.startsWith(c + " ") && !css.endsWith(" " + c) && !css.contains(" " + c + " "))
            css += " " + c;
    }

    /**
     * Sets the standard <code>HTML</code> <code>style</code> attribute.
     * 
     * @param style
     *          the html style
     */
    public void setStyle(String style) {
        this.style = style;
    }

    /**
     * Sets the standard <code>HTML</code> <code>lang</code> attribute.
     * 
     * @param lang
     *          the language
     */
    public void setLang(String lang) {
        this.lang = lang;
    }

    /**
     * Sets the standard <code>HTML</code> <code>dir</code> attribute.
     * 
     * @param dir
     *          the reading direction
     */
    public void setDir(String dir) {
        this.dir = dir;
    }

    /**
     * Sets the standard <code>HTML</code> <code>title</code> attribute which is
     * used to display tooltips.
     * 
     * @param title
     *          the html title
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Sets the standard <code>HTML</code> <code>name</code> attribute.
     * 
     * @param name
     *          the html name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onclick</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnclick(String handler) {
        this.onclick = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onclick</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnClick(String handler) {
        this.onclick = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>ondblclick</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOndblclick(String handler) {
        this.ondblclick = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>ondblclick</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnDblClick(String handler) {
        this.ondblclick = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmousedown</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnmousedown(String handler) {
        this.onmousedown = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmousedown</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnMouseDown(String handler) {
        this.onmousedown = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmousemove</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnmousemove(String handler) {
        this.onmousemove = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmousemove</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnMouseMove(String handler) {
        this.onmousemove = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseout</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnmouseout(String handler) {
        this.onmouseout = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseout</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnMouseOut(String handler) {
        this.onmouseout = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseover</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnmouseover(String handler) {
        this.onmouseover = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseover</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnMouseOver(String handler) {
        this.onmouseover = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseup</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnmouseup(String handler) {
        this.onmouseup = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onmouseup</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnMouseUp(String handler) {
        this.onmouseup = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeydown</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnkeydown(String handler) {
        this.onkeydown = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeydown</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnKeyDown(String handler) {
        this.onkeydown = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeypress</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnkeypress(String handler) {
        this.onkeypress = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeypress</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnKeyPress(String handler) {
        this.onkeypress = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeyup</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnkeyup(String handler) {
        this.onkeyup = handler;
    }

    /**
     * Sets the standard <code>HTML</code> event <code>onkeyup</code> event
     * handler.
     * 
     * @param handler
     *          the event handler code
     */
    public void setOnKeyUp(String handler) {
        this.onkeyup = handler;
    }

    /**
     * Returns the weblounge request. This method searches the probably wrapped
     * request hierarchy for the original weblounge request.
     * 
     * @return the weblounge request
     */
    public WebloungeRequest getRequest() {
        return unwrapRequest(pageContext.getRequest());
    }

    /**
     * Returns the weblounge response. This method searches the probably wrapped
     * response hierarchy for the original weblounge response.
     * 
     * @return the weblounge response
     */
    public WebloungeResponse getResponse() {
        return unwrapResponse(pageContext.getResponse());
    }

    /**
     * Returns the standard attributes ready to inserted in an HTML tag.
     * 
     * @return the standard HTML attributes
     */
    protected Map<String, String> getStandardAttributes() {
        Map<String, String> attributes = new HashMap<String, String>();
        if (css != null)
            attributes.put("class", css);
        if (getId() != null)
            attributes.put("id", getId());
        if (name != null)
            attributes.put("name", name);
        if (style != null)
            attributes.put("style", style);
        if (lang != null)
            attributes.put("lang", lang);
        if (dir != null)
            attributes.put("dir", dir);
        if (title != null)
            attributes.put("title", title);

        // Mouse events
        if (onclick != null)
            attributes.put("onclick", name);
        if (ondblclick != null)
            attributes.put("ondblclick", ondblclick);
        if (onmousedown != null)
            attributes.put("onmousedown", onmousedown);
        if (onmousemove != null)
            attributes.put("onmousemove", onmousemove);
        if (onmouseout != null)
            attributes.put("onmouseout", onmouseout);
        if (onmouseover != null)
            attributes.put("onmouseover", onmouseover);
        if (onmouseup != null)
            attributes.put("onmouseup", onmouseup);

        // Keyboard events
        if (onkeydown != null)
            attributes.put("onkeydown", onkeydown);
        if (onkeypress != null)
            attributes.put("onkeypress", onkeypress);
        if (onkeyup != null)
            attributes.put("onkeyup", onkeyup);

        return attributes;
    }

    /**
     * Overwritten to extract <code>WebloungeRequest</code> and
     * <code>WebloungeResponse</code>.
     * 
     * @see javax.servlet.jsp.tagext.Tag#setPageContext(javax.servlet.jsp.PageContext)
     */
    @Override
    public void setPageContext(PageContext ctxt) {
        super.setPageContext(ctxt);
        request = unwrapRequest(pageContext.getRequest());
        response = unwrapResponse(pageContext.getResponse());
    }

    /**
     * Stores any original value for the given attribute away for later reference.
     * 
     * @param attribute
     *          the attribute name
     */
    protected void stashAttribute(String attribute) {
        stashAndSetAttribute(attribute, null);
    }

    /**
     * Sets the attribute in the page context and stores any original value away
     * for later reference.
     * 
     * @param attribute
     *          the attribute name
     * @param value
     *          the value
     */
    protected void stashAndSetAttribute(String attribute, Object value) {
        Object existingValue = pageContext.getAttribute(attribute);

        // If there is a value already, keep it for later reference
        if (existingValue != null) {
            String existingType = existingValue.getClass().getName();
            logger.debug("Stashing context item '{}' of type {}", attribute, existingType);
            stash.put(attribute, existingValue);
        }

        // Set the attribute
        if (value != null) {
            pageContext.setAttribute(attribute, value);
        }
        attributes.add(attribute);
    }

    /**
     * Removes the given attribute from the page context and replaces it with a
     * potentially stashed value.
     * 
     * @param attribute
     *          the attribute name
     */
    protected void removeAndUnsstashAttribute(String attribute) {
        Object value = stash.remove(attribute);
        if (value != null) {
            String existingType = value.getClass().getName();
            logger.debug("Applying stashed page context item '{}' of type {}", attribute, existingType);
            pageContext.setAttribute(attribute, value);
        } else {
            pageContext.removeAttribute(attribute);
        }
        attributes.remove(attribute);
    }

    /**
     * Removes all attributes that have been set by the current tag instance from
     * the page context and replaces them with potentially stashed values.
     */
    protected void removeAndUnstashAttributes() {
        for (String attribute : attributes) {
            Object value = stash.remove(attribute);
            if (value != null) {
                String existingType = value.getClass().getName();
                logger.debug("Applying stashed page context item '{}' of type {}", attribute, existingType);
                pageContext.setAttribute(attribute, value);
            } else {
                pageContext.removeAttribute(attribute);
            }
        }
        attributes.clear();

        // Make sure we are not missing out on any stash values
        for (Map.Entry<String, Object> stashEntry : stash.entrySet()) {
            String attribute = stashEntry.getKey();
            Object value = stashEntry.getValue();
            String existingType = value.getClass().getName();
            logger.debug("Applying stashed page context item '{}' of type {}", attribute, existingType);
            pageContext.setAttribute(attribute, value);
        }
        stash.clear();
    }

    /**
     * Returns the map of attributes as a string. If there are no attributes in
     * the map, an empty string is returned.
     * 
     * @param attributes
     *          the attributes as a string
     * @return the attributes ready to be added to a tag
     */
    protected static String attributesToString(Map<String, String> attributes) {
        if (attributes == null || attributes.size() == 0)
            return "";
        StringBuffer buf = new StringBuffer();
        for (Map.Entry<String, String> attribute : attributes.entrySet()) {
            if (buf.length() > 0)
                buf.append(" ");
            buf.append(attribute.getKey());
            buf.append("=\"");
            buf.append(attribute.getValue());
            buf.append("\"");
        }
        return buf.toString();
    }

    /**
     * Extract the wrapped <code>WebloungeResponse</code> from a <code>
     * ServletResponse</code>.
     * 
     * @param response
     *          the wrapping response
     * @return the wrapped <code>WebloungeResponse</code> or <code>null</code> if
     *         no such response exists
     */
    private static WebloungeResponse unwrapResponse(ServletResponse response) {
        while (response != null) {
            if (response instanceof WebloungeResponse)
                return (WebloungeResponse) response;
            if (!(response instanceof ServletResponseWrapper))
                break;
            response = ((ServletResponseWrapper) response).getResponse();
        }

        // Last resort
        if (response instanceof HttpServletResponse)
            return new WebloungeResponseImpl((HttpServletResponse) response);

        return null;
    }

    /**
     * Extract the wrapped <code>WebloungeRequest</code> from a <code>
     * ServletRequest</code> .
     * 
     * @param request
     *          the wrapping request
     * @return the wrapped <code>WebloungeRequest</code> or <code>null</code> if
     *         no such response exists
     */
    private static WebloungeRequest unwrapRequest(ServletRequest request) {
        while (request != null) {
            if (request instanceof WebloungeRequest)
                return (WebloungeRequest) request;
            if (!(request instanceof ServletRequestWrapper))
                break;
            request = ((ServletRequestWrapper) request).getRequest();
        }

        // Last resort
        if (request instanceof HttpServletRequest) {
            logger.warn("Found vanilla servlet request, defaulting to production environment");
            // TOOD: Properly determine the environment
            return new WebloungeRequestImpl((HttpServletRequest) request, Environment.Production);
        }

        return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.jsp.tagext.BodyTagSupport#doStartTag()
     */
    @Override
    public int doStartTag() throws JspException {

        // Don't do work if not needed (which is the case during precompilation)
        if (RequestUtils.isPrecompileRequest(request))
            return SKIP_BODY;

        return super.doStartTag();
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.jsp.tagext.TryCatchFinally#doCatch(java.lang.Throwable)
     */
    public void doCatch(Throwable t) throws Throwable {
        // Don't log errors during tag execution while precompiling
        if (RequestUtils.isPrecompileRequest(request)) {
            return;
        }

        logger.warn("Error executing jsp tag {} on {}", getClass().getName(), request.getUrl());
        logger.warn(t.getMessage(), t);
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.jsp.tagext.TryCatchFinally#doFinally()
     */
    public void doFinally() {
        // Make sure the state of the tag is reset between every use
        reset();
    }

}