001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.html;
020:
021: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
022:
023: import org.apache.beehive.netui.util.Bundle;
024:
025: import javax.servlet.jsp.JspException;
026: import javax.servlet.jsp.tagext.JspTag;
027: import javax.servlet.jsp.tagext.SimpleTagSupport;
028: import java.text.DecimalFormat;
029: import java.util.Locale;
030:
031: /**
032: * A formatter used to format numbers. This formatter uses patterns that conform to
033: * <code>java.text.NumberFormat</code> pattern syntax. FormatNumber calls <code>toString()</code> on
034: * the object to be formatted to get the value the pattern is applied to.
035: *
036: * The valid FormatNumber types are:
037: * <ul>
038: * <li>number</li>
039: * <li>currency</li>
040: * <li>percent</li>
041: * </ul>
042: * @jsptagref.tagdescription A formatter used to format numbers.
043: *
044: * <p>The <netui:formatNumber> tag formats the output of its parent tag. For example:
045: *
046: * <pre> <netui:span value="${pageFlow.price}">
047: * <netui:formatNumber country="FR" language="fr" type="currency" />
048: * </netui:span></pre>
049: *
050: * <p>The <code>pattern</code> attribute conforms to
051: * {@link java.text.DecimalFormat java.text.DecimalFormat} pattern syntax.
052: *
053: * <p>The <code>pattern</code> attribute uses the comma as a grouping separater.
054: * If many different grouping sizes are specified in one <code>pattern</code>,
055: * the right-most grouping interval will be used throughout; the other grouping intervals
056: * will be ignored. For example, the following format patterns all produce the same result.
057: * If the number to format is 123456789, each will produce 123,456,789.
058: * <blockquote>
059: * <ul>
060: * <li>pattern="#,##,###,###"</li>
061: * <li>pattern="######,###"</li>
062: * <li>pattern="##,####,###"</li>
063: * </ul>
064: * </blockquote>
065: *
066: * <p>The <code>type</code> attribute specifies three common
067: * kinds of formatting to be applied to the number.
068: * The valid values for the <code>type</code> attribute are:
069: * <blockquote>
070: * <ul>
071: * <li><code>number</code></li>
072: * <li><code>currency</code></li>
073: * <li><code>percent</code></li>
074: * </ul>
075: * </blockquote>
076: *
077: * <p>The <code>country</code> attribute takes an upper-case,
078: * two-letter code as defined by ISO-3166.
079: *
080: * <p>The <code>language</code> attribute takes a lower-case,
081: * two-letter code as defined by ISO-639.
082: * @example In this first example, the value "12345678" is formatted
083: * to 12,345,678.00.
084: * <pre> <netui:span value="12345678">
085: * <netui:formatNumber pattern="#,###.00" />
086: * </netui:span></pre>
087: *
088: * <p>In the next sample, the value ".33" is formatted to 33%.</p>
089: * <pre> <netui:span value=".33">
090: * <netui:formatNumber type="percent" />
091: * </netui:span></pre>
092: *
093: * <p>In the next sample, the value "14.317" is formatted
094: * to $14.32.</p>
095: * <pre> <netui:span value="14.317">
096: * <netui:formatNumber country="US" language="en" type="currency" />
097: * </netui:span></pre>
098: * @netui:tag name="formatNumber" body-content="empty" description="A formatter used to format numbers."
099: */
100: public class FormatNumber extends FormatTag {
101: /**
102: * The type of number format to be used.
103: */
104: protected String _type;
105:
106: /**
107: * Return the name of the Tag.
108: */
109: public String getTagName() {
110: return "FormatNumber";
111: }
112:
113: /**
114: * Sets the type of number format to be used (number, currency, or percent).
115: * @param type the number format type.
116: * @jsptagref.attributedescription The type of the format to apply. Possible values are <code>number</code>, <code>currency</code>, or <code>percent</code>.
117: * @jsptagref.databindable false
118: * @jsptagref.attributesyntaxvalue <i>string_type</i>
119: * @netui:attribute required="false" rtexprvalue="true"
120: * description="The type of the format to apply. Possible values are number, currency, or percent."
121: */
122: public void setType(String type) throws JspException {
123: _type = setRequiredValueAttribute(type, "type");
124: if (_type != null) {
125: if (!type.equals("number") && !type.equals("currency")
126: && !type.equals("percent")) {
127: String s = Bundle
128: .getString("Tags_NumberFormatWrongType");
129: registerTagError(s, null);
130: }
131: }
132: }
133:
134: /**
135: * Create the internal Formatter instance and perform the formatting.
136: * @throws JspException if a JSP exception has occurred
137: */
138: public void doTag() throws JspException {
139: JspTag parentTag = SimpleTagSupport.findAncestorWithClass(this ,
140: IFormattable.class);
141:
142: // if there are errors we need to either add these to the parent AbstractBastTag or report an error.
143: if (hasErrors()) {
144: if (parentTag instanceof IFormattable) {
145: IFormattable parent = (IFormattable) parentTag;
146: parent.formatterHasError();
147: }
148: reportErrors();
149: return;
150: }
151:
152: // if there are no errors then add this to the parent as a formatter.
153: if (parentTag instanceof IFormattable) {
154: NumberFormatter formatter = new NumberFormatter();
155: formatter.setPattern(_pattern);
156: formatter.setType(_type);
157: formatter.setLocale(getLocale());
158: IFormattable parent = (IFormattable) parentTag;
159: parent.addFormatter(formatter);
160: } else {
161: String s = Bundle
162: .getString("Tags_FormattableParentRequired");
163: registerTagError(s, null);
164: reportErrors();
165: }
166: }
167:
168: /**
169: * Internal FormatTag.Formatter which uses NumberFormat.
170: */
171: public static class NumberFormatter extends FormatTag.Formatter {
172: private String type;
173: private Locale locale;
174:
175: public void setType(String type) {
176: this .type = type;
177: }
178:
179: public void setLocale(Locale locale) {
180: this .locale = locale;
181: }
182:
183: public String format(Object dataToFormat) throws JspException {
184: if (dataToFormat == null) {
185: return null;
186: }
187: InternalStringBuilder formattedString = new InternalStringBuilder(
188: 32);
189: DecimalFormat numberFormat = null;
190:
191: // get the number format. The type has been validated when it was set on the tag.
192: if (locale == null) {
193: if ((type == null) || (type.equals("number"))) {
194: numberFormat = (DecimalFormat) java.text.NumberFormat
195: .getNumberInstance();
196: } else if (type.equals("currency")) {
197: numberFormat = (DecimalFormat) java.text.NumberFormat
198: .getCurrencyInstance();
199: } else if (type.equals("percent")) {
200: numberFormat = (DecimalFormat) java.text.NumberFormat
201: .getPercentInstance();
202: } else {
203: assert (false) : "Invalid type was found:" + type;
204: }
205: } else {
206: if ((type == null) || (type.equals("number"))) {
207: numberFormat = (DecimalFormat) java.text.NumberFormat
208: .getNumberInstance(locale);
209: } else if (type.equals("currency")) {
210: numberFormat = (DecimalFormat) java.text.NumberFormat
211: .getCurrencyInstance(locale);
212: } else if (type.equals("percent")) {
213: numberFormat = (DecimalFormat) java.text.NumberFormat
214: .getPercentInstance(locale);
215: } else {
216: assert (false) : "Invalid type was found:" + type;
217: }
218: }
219:
220: // format the number, apply the pattern specified
221: try {
222: if (getPattern() != null)
223: numberFormat.applyPattern(getPattern());
224: } catch (Exception e) {
225: JspException jspException = new JspException(Bundle
226: .getString("Tags_NumberFormatPatternException",
227: e.getMessage()), e);
228:
229: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
230: // initCause() throws an IllegalStateException if the cause is already set.
231: if (jspException.getCause() == null) {
232: jspException.initCause(e);
233: }
234: throw jspException;
235: }
236:
237: // parse the number
238: if (dataToFormat.toString().length() == 0) {
239: return "";
240: }
241: try {
242: double number = Double.parseDouble(dataToFormat
243: .toString());
244: formattedString.append(numberFormat.format(number));
245: } catch (Exception e) {
246: JspException jspException = new JspException(Bundle
247: .getString("Tags_NumberFormatParseException", e
248: .getMessage()), e);
249:
250: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
251: // initCause() throws an IllegalStateException if the cause is already set.
252: if (jspException.getCause() == null) {
253: jspException.initCause(e);
254: }
255: throw jspException;
256: }
257:
258: return formattedString.toString();
259:
260: }
261: }
262: }
|