/*
* 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.
*
* $Header:$
*/
package org.apache.beehive.netui.tags.html;
import org.apache.beehive.netui.util.internal.InternalStringBuilder;
import org.apache.beehive.netui.util.Bundle;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.text.DecimalFormat;
import java.util.Locale;
/**
* A formatter used to format numbers. This formatter uses patterns that conform to
* java.text.NumberFormat
pattern syntax. FormatNumber calls toString()
on
* the object to be formatted to get the value the pattern is applied to.
*
* The valid FormatNumber types are:
*
The <netui:formatNumber> tag formats the output of its parent tag. For example: * *
<netui:span value="${pageFlow.price}"> * <netui:formatNumber country="FR" language="fr" type="currency" /> * </netui:span>* *
The pattern
attribute conforms to
* {@link java.text.DecimalFormat java.text.DecimalFormat} pattern syntax.
*
*
The pattern
attribute uses the comma as a grouping separater.
* If many different grouping sizes are specified in one pattern
,
* the right-most grouping interval will be used throughout; the other grouping intervals
* will be ignored. For example, the following format patterns all produce the same result.
* If the number to format is 123456789, each will produce 123,456,789.
*
** **
*- pattern="#,##,###,###"
*- pattern="######,###"
*- pattern="##,####,###"
*
The type
attribute specifies three common
* kinds of formatting to be applied to the number.
* The valid values for the type
attribute are:
*
** **
*- *
number
- *
currency
- *
percent
The country
attribute takes an upper-case,
* two-letter code as defined by ISO-3166.
*
*
The language
attribute takes a lower-case,
* two-letter code as defined by ISO-639.
* @example In this first example, the value "12345678" is formatted
* to 12,345,678.00.
*
<netui:span value="12345678"> * <netui:formatNumber pattern="#,###.00" /> * </netui:span>* *
In the next sample, the value ".33" is formatted to 33%.
*<netui:span value=".33"> * <netui:formatNumber type="percent" /> * </netui:span>* *
In the next sample, the value "14.317" is formatted * to $14.32.
*<netui:span value="14.317"> * <netui:formatNumber country="US" language="en" type="currency" /> * </netui:span>* @netui:tag name="formatNumber" body-content="empty" description="A formatter used to format numbers." */ public class FormatNumber extends FormatTag { /** * The type of number format to be used. */ protected String _type; /** * Return the name of the Tag. */ public String getTagName() { return "FormatNumber"; } /** * Sets the type of number format to be used (number, currency, or percent). * @param type the number format type. * @jsptagref.attributedescription The type of the format to apply. Possible values are
number
, currency
, or percent
.
* @jsptagref.databindable false
* @jsptagref.attributesyntaxvalue string_type
* @netui:attribute required="false" rtexprvalue="true"
* description="The type of the format to apply. Possible values are number, currency, or percent."
*/
public void setType(String type)
throws JspException
{
_type = setRequiredValueAttribute(type, "type");
if (_type != null) {
if (!type.equals("number") && !type.equals("currency") && !type.equals("percent")) {
String s = Bundle.getString("Tags_NumberFormatWrongType");
registerTagError(s, null);
}
}
}
/**
* Create the internal Formatter instance and perform the formatting.
* @throws JspException if a JSP exception has occurred
*/
public void doTag()
throws JspException
{
JspTag parentTag = SimpleTagSupport.findAncestorWithClass(this, IFormattable.class);
// if there are errors we need to either add these to the parent AbstractBastTag or report an error.
if (hasErrors()) {
if (parentTag instanceof IFormattable) {
IFormattable parent = (IFormattable) parentTag;
parent.formatterHasError();
}
reportErrors();
return;
}
// if there are no errors then add this to the parent as a formatter.
if (parentTag instanceof IFormattable) {
NumberFormatter formatter = new NumberFormatter();
formatter.setPattern(_pattern);
formatter.setType(_type);
formatter.setLocale(getLocale());
IFormattable parent = (IFormattable) parentTag;
parent.addFormatter(formatter);
}
else {
String s = Bundle.getString("Tags_FormattableParentRequired");
registerTagError(s, null);
reportErrors();
}
}
/**
* Internal FormatTag.Formatter which uses NumberFormat.
*/
public static class NumberFormatter extends FormatTag.Formatter
{
private String type;
private Locale locale;
public void setType(String type)
{
this.type = type;
}
public void setLocale(Locale locale)
{
this.locale = locale;
}
public String format(Object dataToFormat) throws JspException
{
if (dataToFormat == null) {
return null;
}
InternalStringBuilder formattedString = new InternalStringBuilder(32);
DecimalFormat numberFormat = null;
// get the number format. The type has been validated when it was set on the tag.
if (locale == null) {
if ((type == null) || (type.equals("number"))) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getNumberInstance();
}
else if (type.equals("currency")) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getCurrencyInstance();
}
else if (type.equals("percent")) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getPercentInstance();
}
else {
assert(false) : "Invalid type was found:" + type;
}
}
else {
if ((type == null) || (type.equals("number"))) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getNumberInstance(locale);
}
else if (type.equals("currency")) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getCurrencyInstance(locale);
}
else if (type.equals("percent")) {
numberFormat = (DecimalFormat) java.text.NumberFormat.getPercentInstance(locale);
}
else {
assert(false) : "Invalid type was found:" + type;
}
}
// format the number, apply the pattern specified
try {
if (getPattern() != null)
numberFormat.applyPattern(getPattern());
}
catch (Exception e) {
JspException jspException = new JspException(Bundle.getString("Tags_NumberFormatPatternException", e.getMessage()), e);
// The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
// initCause() throws an IllegalStateException if the cause is already set.
if (jspException.getCause() == null) {
jspException.initCause(e);
}
throw jspException;
}
// parse the number
if (dataToFormat.toString().length() == 0) {
return "";
}
try {
double number = Double.parseDouble(dataToFormat.toString());
formattedString.append(numberFormat.format(number));
}
catch (Exception e) {
JspException jspException = new JspException(Bundle.getString("Tags_NumberFormatParseException", e.getMessage()), e);
// The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
// initCause() throws an IllegalStateException if the cause is already set.
if (jspException.getCause() == null) {
jspException.initCause(e);
}
throw jspException;
}
return formattedString.toString();
}
}
}