/*
 * $Id: WriteTag.java,v 1.7 2006/05/31 14:00:31 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: WriteTag.java,v $
 * Revision 1.7  2006/05/31 14:00:31  marbobo
 * + Zeilenumbrüche bei Beschreibungsfelder anzeigen
 *
 * Revision 1.6  2006/05/24 17:53:58  marbobo
 * + vkc:write-Tag erweitert (stripHTML)
 *
 * Revision 1.5  2006/05/09 15:26:42  marbobo
 * + vkc:write-Tag um Darstellung von Html-Text erweitert
 *
 * Revision 1.4  2006/03/09 01:34:15  marbobo
 * + Unicode-Unterstützung für Javascript implementiert
 *
 * Revision 1.3  2006/01/20 12:44:58  korowa
 * *** empty log message ***
 *
 * Revision 1.2  2005/02/03 23:26:17  marbobo
 * + Zusätzliches Attribute hinzugefügt
 *
 * Revision 1.1  2005/02/02 22:59:29  marbobo
 * + Initiale Version
 *
 */
package vkc.view.tag;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import javax.swing.text.BadLocationException;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.ResponseUtils;

import vkc.util.StringUtils;
import vkc.util.VKCStringUtils;

public class WriteTag extends TagSupport {
    /**
     * The key to search default format string for java.sql.Timestamp in resources.
     */
    public static final String SQL_TIMESTAMP_FORMAT_KEY = "org.apache.struts.taglib.bean.format.sql.timestamp";

    /**
     * The key to search default format string for java.sql.Date in resources.
     */
    public static final String SQL_DATE_FORMAT_KEY = "org.apache.struts.taglib.bean.format.sql.date";

    /**
     * The key to search default format string for java.sql.Time in resources.
     */
    public static final String SQL_TIME_FORMAT_KEY = "org.apache.struts.taglib.bean.format.sql.time";

    /**
     * The key to search default format string for java.util.Date in resources.
     */
    public static final String DATE_FORMAT_KEY = "org.apache.struts.taglib.bean.format.date";

    /**
     * The key to search default format string for int (byte, short, etc.) in resources.
     */
    public static final String INT_FORMAT_KEY = "org.apache.struts.taglib.bean.format.int";

    /**
     * The key to search default format string for float (double, BigDecimal) in
     * resources.
     */
    public static final String FLOAT_FORMAT_KEY = "org.apache.struts.taglib.bean.format.float";

    /**
     * The message resources for this package.
     */
    protected static MessageResources messages = MessageResources.getMessageResources("org.apache.struts.taglib.bean.LocalStrings");

    // ------------------------------------------------------------- Properties
    protected boolean javascript = false;
    
    public boolean getJavascript() {
    	return javascript;
    }
    
    public void setJavascript(boolean javascript) {
    	this.javascript = javascript;
    }
    
    /**
     * Filter the rendered output for characters that are sensitive in HTML?
     */
    protected boolean filter = true;

    public boolean getFilter() {
        return filter;
    }

    public void setFilter(boolean filter) {
        this.filter = filter;
    }

    protected boolean displayAsHTML = false;

    public boolean getDisplayAsHTML() {
        return displayAsHTML;
    }

    public void setDisplayAsHTML(boolean displayAsHTML) {
        this.displayAsHTML = displayAsHTML;
    }

    protected boolean stripHTML = false;

    public boolean getStripHTML() {
        return stripHTML;
    }

    public void setStripHTML(boolean stripHTML) {
        this.stripHTML = stripHTML;
    }

    /**
     * Should we ignore missing beans and simply output nothing?
     */
    protected boolean ignore = false;

    public boolean getIgnore() {
        return ignore;
    }

    public void setIgnore(boolean ignore) {
        this.ignore = ignore;
    }

    /**
     * Name of the bean that contains the data we will be rendering.
     */
    protected String name = null;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * Name of the property to be accessed on the specified bean.
     */
    protected String property = null;

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    /**
     * The scope to be searched to retrieve the specified bean.
     */
    protected String scope = null;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    /**
     * The format string to be used as format to convert
     * value to String.
     */
    protected String formatStr = null;

    public String getFormat() {
        return formatStr;
    }

    public void setFormat(String formatStr) {
        this.formatStr = formatStr;
    }

    /**
     * The key to search format string in applciation resources
     */
    protected String formatKey = null;

    public String getFormatKey() {
        return formatKey;
    }

    public void setFormatKey(String formatKey) {
        this.formatKey = formatKey;
    }

    /**
     * The session scope key under which our Locale is stored.
     */
    protected String localeKey = null;

    public String getLocale() {
        return localeKey;
    }

    public void setLocale(String localeKey) {
        this.localeKey = localeKey;
    }

    /**
     * The servlet context attribute key for our resources.
     */
    protected String bundle = null;

    public String getBundle() {
        return bundle;
    }

    public void setBundle(String bundle) {
        this.bundle = bundle;
    }

    /**
     * Renders New Line in HTML?
     */
    protected boolean renderNewLine = false;

    public boolean getRenderNewLine() {
        return renderNewLine;
    }

    public void setRenderNewLine(boolean renderNewLine) {
        this.renderNewLine = renderNewLine;
    }

    // --------------------------------------------------------- Public Methods

    /**
     * Process the start tag.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doStartTag() throws JspException {
        // Look up the requested bean (if necessary)
        if (ignore) {
            if (RequestUtils.lookup(pageContext, name, scope) == null) {
                return SKIP_BODY; // Nothing to output
            }
        }

        // Look up the requested property value
        Object value = RequestUtils.lookup(pageContext, name, property, scope);

        if (value == null) {
            return SKIP_BODY; // Nothing to output
        }

        // Convert value to the String with some formatting
        String output = formatValue(value);
        
        if (javascript) {
        	output = StringUtils.replaceToken(output, "\n", "\\n");
        	output = StringUtils.replaceToken(output, "\r", "\\r");
			
			if (!filter) {
				output = VKCStringUtils.decodeXmlEntities(output);
			}
			
			output = VKCStringUtils.filterJavascriptChars(output);
        }

        if (stripHTML) {
        	output = VKCStringUtils.filterUnicodeChars(output);
        	output = VKCStringUtils.htmlFormatedText(output);
        	output = VKCStringUtils.decodeXmlEntities(output);
			output = VKCStringUtils.decodeNamedXmlEntitiesNotInSwing(output);
            output = VKCStringUtils.encodeNewLine(output);
        	
        	try {
				output = VKCStringUtils.removeHtmlTags(output);
			} catch (BadLocationException e) {
				e.printStackTrace();
			}

            output = VKCStringUtils.decodeNewLine(output);
        }

        // Print this property value to our output writer, suitably filtered
        if (filter) {
            output = VKCStringUtils.filterUnicodeChars(output);
        }

        if (renderNewLine) {
            output = VKCStringUtils.convertNewLineInHTML(output);
        }
        
        if (displayAsHTML) {
            output = VKCStringUtils.htmlFormatedText(output);
        }

        ResponseUtils.write(pageContext, output);

        // Continue processing this page
        return SKIP_BODY;
    }

    /**
     * Retrieve format string from message bundle and return null if
     * message not found or message string.
     *
     * @param formatKey value to use as key to search message in bundle
     * @exception JspException if a JSP exception has occurred
     */
    protected String retrieveFormatString(String formatKey) throws JspException {
        String result = RequestUtils.message(pageContext, this.bundle, this.localeKey, formatKey);

        if ((result != null) && !(result.startsWith("???") && result.endsWith("???"))) {
            return result;
        }
        
        return null;
    }

    /**
     * Format value according to specified format string (as tag attribute or
     * as string from message resources) or to current user locale.
     *
     * When a format string is retrieved from the message resources,
     * <code>applyLocalizedPattern</code> is used. For more about localized
     * patterns, see
     * <http://www.dei.unipd.it/corsi/fi2ae-docs/source/jdk1.1.7/src/java/text/resources/>.
     * (To obtain the correct value for some characters, you may need to view
     * the file in a hex editor and then use the Unicode escape form in the
     * property resources file.)
     *
     * @param valueToFormat value to process and convert to String
     * @exception JspException if a JSP exception has occurred
     */
    protected String formatValue(Object valueToFormat) throws JspException {
        Format format = null;
        Object value = valueToFormat;
        Locale locale = RequestUtils.retrieveUserLocale(pageContext, this.localeKey);
        boolean formatStrFromResources = false;
        String formatString = formatStr;

        // Return String object as is.
        if (value instanceof java.lang.String) {
            return (String) value;
        }
        
        // Try to retrieve format string from resources by the key from formatKey.
        if (formatString == null && formatKey != null) {
            formatString = retrieveFormatString(this.formatKey);
            
            if (formatString != null) {
                formatStrFromResources = true;
            }
        }

        // Prepare format object for numeric values.
        if (value instanceof Number) {
            if (formatString == null) {
                if ((value instanceof Byte)
                  || (value instanceof Short)
                  || (value instanceof Integer)
                  || (value instanceof Long)
                  || (value instanceof BigInteger)) {
                    formatString = retrieveFormatString(INT_FORMAT_KEY);
                } else if ((value instanceof Float)
                         || (value instanceof Double)
                         || (value instanceof BigDecimal)) {
                    formatString = retrieveFormatString(FLOAT_FORMAT_KEY);
                }

                if (formatString != null) {
                    formatStrFromResources = true;
                }
            }

            if (formatString != null) {
                try {
                    format = NumberFormat.getNumberInstance(locale);
                    
                    if (formatStrFromResources) {
                        ((DecimalFormat) format).applyLocalizedPattern(formatString);
                    } else {
                        ((DecimalFormat) format).applyPattern(formatString);
                    }
                } catch (IllegalArgumentException e) {
                    JspException ex = new JspException(messages.getMessage("write.format", formatString));
                    RequestUtils.saveException(pageContext, ex);
                    throw ex;
                }
            }
        } else if (value instanceof java.util.Date) {
            if (formatString == null) {
                if (value instanceof java.sql.Timestamp) {
                    formatString = retrieveFormatString(SQL_TIMESTAMP_FORMAT_KEY);
                } else if (value instanceof java.sql.Date) {
                    formatString = retrieveFormatString(SQL_DATE_FORMAT_KEY);
                } else if (value instanceof java.sql.Time) {
                    formatString = retrieveFormatString(SQL_TIME_FORMAT_KEY);
                } else if (value instanceof java.util.Date) {
                    formatString = retrieveFormatString(DATE_FORMAT_KEY);
                }
            }

            if (formatString != null) {
            	format = new SimpleDateFormat(formatString, locale);
            }
        }

        if (format != null) {
            return format.format(value);
        } 

        return value.toString();
    }

    /**
     * Release all allocated resources.
     */
    public void release() {
        super.release();
        javascript = false;
        filter = true;
        displayAsHTML = false;
        stripHTML = false;
        ignore = false;
        name = null;
        property = null;
        scope = null;
        formatStr = null;
        formatKey = null;
        localeKey = null;
        bundle = null;
    }
}