coliver 2003/08/08 11:49:06
Modified: src/documentation/xdocs/userdocs/flow jxtemplate.xml
src/java/org/apache/cocoon/generation
JXTemplateGenerator.java
Log:
Added JSTL formatNumber and formatDate tags
Revision Changes Path
1.18 +60 -1
cocoon-2.1/src/documentation/xdocs/userdocs/flow/jxtemplate.xml
Index: jxtemplate.xml
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/documentation/xdocs/userdocs/flow/jxtemplate.xml,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- jxtemplate.xml 20 Jul 2003 05:48:18 -0000 1.17
+++ jxtemplate.xml 8 Aug 2003 18:49:06 -0000 1.18
@@ -233,12 +233,71 @@
</source>
<p/>
</s2>
+<s2 title="formatNumber">
+<p>
+The <code>formatNumber</code> tag is used to display numeric data, including
currencies and percentages, in a locale-specific manner. It determines from the
locale, for example, whether to use a period or a comma for delimiting the
integer and decimal portions of a number. Here is its syntax:
+</p>
+<p><source>
+<formatNumber value="Expression"
+ [type="Type"] [pattern="Expression"]
+ [currencyCode="Expression"] [currencySymbol="Expression"]
+ [maxIntegerDigits="Expression"] [minIntegerDigits="Expression"]
+ [maxFractionDigits="Expression"] [minFractionDigits="Expression"]
+ [groupingUsed="Expression"]
+ [var="Name"] [locale="Expression"]>
+</source>
+</p>
+<p>
+Only the <code>value</code> attribute is required. It is used to specify the
numeric value that is to be formatted.
+</p>
+<p>
+The value of the <code>type</code> attribute should be either "number",
"currency", or "percentage", and indicates what type of numeric value is being
formatted. The default value for this attribute is "number". The
<code>pattern</code> attribute takes precedence over the <code>type</code>
attribute and allows more precise formatting of numeric values following the
pattern conventions of the <code>java.text.DecimalFormat</code> class.
+</p>
+<p>
+When the <code>type</code> attribute has a value of "currency", the
<code>currencyCode</code> attribute can be used to explicitly specify the
currency for the numerical value being displayed. As with language and country
codes, currency codes are governed by an ISO standard. This code is used to
determine the currency symbol to display as part of the formatted value.
+</p>
+<p>
+Alternatively, you can use the <code>currencySymbol</code> attribute to
explicitly specify the currency symbol. Note that as of JDK 1.4 and the
associated introduction of the <code>java.util.Currency</code> class, the
<code>currencyCode</code> attribute of <code>formatNumber</code> takes
precedence over the <code>currencySymbol</code> attribute. For earlier versions
of the JDK, however, the <code>currencySymbol</code> attribute takes
precedence.
+</p>
+<p>
+The <code>maxIntegerDigits</code>, <code>minIntegerDigits</code>,
<code>maxFractionDigits</code>, and <code>minFractionDigits</code> attributes
are used to control the number of significant digits displayed before and after
the decimal point. These attributes require integer values.
+</p>
+<p>
+The <code>groupingUsed</code> attribute takes a <code>Boolean</code> value
and controls whether digits before the decimal point are grouped. For example,
in English-language locales, large numbers have their digits grouped by threes,
with each set of three delimited by a comma. Other locales delimit such
groupings with a period or a space. The default value for this attribute is
<code>true</code>.
+</p>
+
+</s2>
+<s2 title="formatDate">
+<p>The <code>formatDate</code> tag provides facilities to format Date
values:</p>
+<p>
+<source>
+<formatDate value="Expression" [dateStyle="Style"]
+ [timeStyle="Style"] [pattern="Expression"] [type="Type"] [var="Name"]
+ [locale="Expression"]>
+</source>
+</p>
+<p>
+Only the value attribute is required. Its value should be an instance of the
<code>java.util.Date</code> class, specifying the date and/or time data to be
formatted and displayed.</p>
+
+<p>The optional <code>timeZone</code> attribute indicates the time zone in
which the date and/or time are to be displayed. If not present, then the JVM's
default time zone is used (that is, the time zone setting specified for the
local operating system). </p>
+<p>
+The <code>type</code> attribute indicates which fields of the specified
<code>Date</code> instance are to be displayed, and should be either "time",
"date", or "both". The default value for this attribute is "date", so if no
<code>type</code> attribute is present, the <code>formatDate</code> tag -- true
to its name -- will only display the date information associated with the
<code>Date</code> instance, specified using the tag's value attribute.
+</p>
+<p>
+The <code>dateStyle</code> and <code>timeStyle</code> attributes indicate
how the date and time information should be formatted, respectively. Valid
styles are "default", "short", "medium", "long", and "full". The default value
is, naturally, "default", indicating that a locale-specific style should be
used. The semantics for the other four style values are as defined by the
@link{java.text.DateFormat} class.
+</p>
+<p>
+Rather than relying on the built-in styles, you can use the pattern
attribute to specify a custom style. When present, the value of the pattern
attribute should be a pattern string following the conventions of the
@link{java.text.SimpleDateFormat} class. These patterns are based on replacing
designated characters within the pattern with corresponding date and time
fields. For example, the pattern MM/dd/yyyy indicates that two-digit month and
date values and a four-digit year value should be displayed, separated by
forward slashes.
+</p>
+<p>
+If the <code>var</code> attribute is specified, then a String value
containing the formatted date is assigned to the named variable. Otherwise, the
<code>formatDate</code> tag will write out the formatting results.
+</p>
+</s2>
<s2 title="macro">
<p>The <code>macro</code> tag allows you define a new custom tag.</p><source>
<macro name="Name" [targetNamespace="Namespace"]>
<parameter name="Name" [optional="Boolean"] [default="Value"]/>*
body
-
</macro>
</source>
<p> For example:</p><source>
1.5 +678 -13
cocoon-2.1/src/java/org/apache/cocoon/generation/JXTemplateGenerator.java
Index: JXTemplateGenerator.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/java/org/apache/cocoon/generation/JXTemplateGenerator.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- JXTemplateGenerator.java 6 Aug 2003 11:31:01 -0000 1.4
+++ JXTemplateGenerator.java 8 Aug 2003 18:49:06 -0000 1.5
@@ -99,6 +99,8 @@
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.LocatorImpl;
+import java.util.*;
+import java.text.*;
/**
* <p>(<em>JX</em> for <a
href="http://jakarta.apache.org/commons/jxpath">Apache <em>JX</em>Path</a>
@@ -241,6 +243,28 @@
* Either <code>items</code> or both <code>begin</code> and <code>end</code>
* must be present.<p>
*
+ *
+ *
+ * <p>
+ * The <code>formatNumber</code> tag is used to display numeric data,
including currencies and percentages, in a locale-specific manner. The
<code>formatNumber</code>> action determines from the locale, for example,
whether to use a period or a comma for delimiting the integer and decimal
portions of a number. Here is its syntax:
+ * </p>
+ * <p>
+ * <formatNumber value="Expression"
+ * [type="Type"] [pattern="Expression"]
+ * [currencyCode="Expression"] [currencySymbol="Expression"]
+ * [maxIntegerDigits="Expression"] [minIntegerDigits="Expression"]
+ * [maxFractionDigits="Expression"] [minFractionDigits="Expression"]
+ * [groupingUsed="Expression"]
+ * [var="Name"] [locale="Expression"]>
+ * </p>
+ *
+ * <p>The <code>formatDate</code> tag provides facilities to format Date
values:</p>
+ *<p>
+ * <formatDate value="Expression" [dateStyle="Style"]
+ [timeStyle="Style"] [pattern="Expression"] [type="Type"] [var="Name"]
+ [locale="Expression"]>
+ * </p>
+ *
* <p>The <code>macro</code> tag allows you define a new custom tag.</p>
*
* <p><pre>
@@ -389,8 +413,13 @@
if (result == Undefined.instance ||
result == ScriptableObject.NOT_FOUND) {
result = null;
- } else while (result instanceof Wrapper) {
- result = ((Wrapper)result).unwrap();
+
+ } else {
+ if (!(result instanceof NativeJavaClass)) {
+ while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ }
}
return result;
} catch (JavaScriptException e) {
@@ -437,8 +466,11 @@
if (result == Undefined.instance ||
result == ScriptableObject.NOT_FOUND) {
result = null;
- } else while (result instanceof Wrapper) {
- result = ((Wrapper)result).unwrap();
+ }
+ if (result instanceof Wrapper) {
+ if (!(result instanceof NativeJavaClass)) {
+ result = ((Wrapper)result).unwrap();
+ }
}
return result;
} finally {
@@ -520,8 +552,12 @@
if (result == Undefined.instance ||
result == ScriptableObject.NOT_FOUND) {
result = null;
- } else while (result instanceof Wrapper) {
- result = ((Wrapper)result).unwrap();
+ } else {
+ if (!(result instanceof NativeJavaClass)) {
+ while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ }
}
return result;
} finally {
@@ -559,8 +595,12 @@
if (result == Undefined.instance ||
result == ScriptableObject.NOT_FOUND) {
result = null;
- } else while (result instanceof Wrapper) {
- result = ((Wrapper)result).unwrap();
+ } else {
+ if (!(result instanceof NativeJavaClass)) {
+ while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ }
}
return result;
} finally {
@@ -741,6 +781,8 @@
final static String SET = "set";
final static String MACRO = "macro";
final static String PARAMETER = "parameter";
+ final static String FORMAT_NUMBER = "formatNumber";
+ final static String FORMAT_DATE = "formatDate";
/**
@@ -811,12 +853,34 @@
throws SAXException {
Expression res = compileExpr(val, msg, location);
if (res == null) return null;
- if (res.compiledExpression != null) {
+ if (res.compiledExpression == null) {
res.compiledExpression = Integer.valueOf(res.raw);
}
return res;
}
+ // Compile an numeric expression (returns either a Compiled Expression
+ // or an Number literal)
+ private static Expression compileNumber(String val, String msg, Locator
location)
+ throws SAXException {
+ Expression res = compileExpr(val, msg, location);
+ if (res == null) return null;
+ if (res.compiledExpression == null) {
+ res.compiledExpression = Integer.valueOf(res.raw);
+ }
+ return res;
+ }
+
+ private static Expression compileBoolean(String val, String msg, Locator
location)
+ throws SAXException {
+ Expression res = compileExpr(val, msg, location);
+ if (res == null) return null;
+ if (res.compiledExpression == null) {
+ res.compiledExpression = Boolean.valueOf(res.raw);
+ }
+ return res;
+ }
+
private static Expression compile(final String variable, boolean xpath)
throws Exception {
Object compiled;
@@ -828,9 +892,10 @@
return new Expression(variable, compiled);
}
- private Object getValue(Expression expr, JexlContext jexlContext,
+ static private Object getValue(Expression expr, JexlContext jexlContext,
JXPathContext jxpathContext)
throws Exception {
+ if (expr == null) return null;
Object compiled = expr.compiledExpression;
try {
if (compiled instanceof CompiledExpression) {
@@ -851,7 +916,7 @@
}
}
- private int getIntValue(Expression expr, JexlContext jexlContext,
+ static private int getIntValue(Expression expr, JexlContext jexlContext,
JXPathContext jxpathContext)
throws Exception {
Object res = getValue(expr, jexlContext, jxpathContext);
@@ -861,6 +926,42 @@
return 0;
}
+ static private Number getNumberValue(Expression expr, JexlContext
jexlContext,
+ JXPathContext jxpathContext)
+ throws Exception {
+ Object res = getValue(expr, jexlContext, jxpathContext);
+ if (res instanceof Number) {
+ return (Number)res;
+ }
+ if (res == null) {
+ return null;
+ }
+ return Double.valueOf(res.toString());
+ }
+
+ static private String getStringValue(Expression expr, JexlContext
jexlContext,
+ JXPathContext jxpathContext)
+ throws Exception {
+ Object res = getValue(expr, jexlContext, jxpathContext);
+ if (res != null) {
+ return res.toString();
+ }
+ if (expr != null) {
+ return expr.raw;
+ }
+ return null;
+ }
+
+ static private Boolean getBooleanValue(Expression expr, JexlContext
jexlContext,
+ JXPathContext jxpathContext)
+ throws Exception {
+ Object res = getValue(expr, jexlContext, jxpathContext);
+ if (res instanceof Boolean) {
+ return (Boolean)res;
+ }
+ return null;
+ }
+
// Hack: try to prevent JXPath from converting result to a String
private Object getNode(Expression expr, JexlContext jexlContext,
JXPathContext jxpathContext)
@@ -1503,6 +1604,450 @@
final Expression value;
}
+ // formatNumber tag (borrows from Jakarta taglibs JSTL)
+
+ private static final char HYPHEN = '-';
+ private static final char UNDERSCORE = '_';
+
+ private static Locale parseLocale(String locale, String variant) {
+
+ Locale ret = null;
+ String language = locale;
+ String country = null;
+ int index = -1;
+
+ if (((index = locale.indexOf(HYPHEN)) > -1)
+ || ((index = locale.indexOf(UNDERSCORE)) > -1)) {
+ language = locale.substring(0, index);
+ country = locale.substring(index+1);
+ }
+
+ if ((language == null) || (language.length() == 0)) {
+ throw new IllegalArgumentException("No language in locale");
+ }
+
+ if (country == null) {
+ if (variant != null)
+ ret = new Locale(language, "", variant);
+ else
+ ret = new Locale(language, "");
+ } else if (country.length() > 0) {
+ if (variant != null)
+ ret = new Locale(language, country, variant);
+ else
+ ret = new Locale(language, country);
+ } else {
+ throw new IllegalArgumentException("Empty country in locale");
+
+ }
+
+ return ret;
+ }
+
+ private static final String NUMBER = "number";
+ private static final String CURRENCY = "currency";
+ private static final String PERCENT = "percent";
+
+ static class StartFormatNumber extends StartInstruction {
+
+ Expression value;
+ Expression type;
+ Expression pattern;
+ Expression currencyCode;
+ Expression currencySymbol;
+ Expression isGroupingUsed;
+ Expression maxIntegerDigits;
+ Expression minIntegerDigits;
+ Expression maxFractionDigits;
+ Expression minFractionDigits;
+ Expression locale;
+
+ Expression var;
+
+ private static Class currencyClass;
+
+ static {
+ try {
+ currencyClass = Class.forName("java.util.Currency");
+ // container's runtime is J2SE 1.4 or greater
+ } catch (Exception cnfe) {
+ }
+ }
+
+ public StartFormatNumber(StartElement raw,
+ Expression var,
+ Expression value,
+ Expression type,
+ Expression pattern,
+ Expression currencyCode,
+ Expression currencySymbol,
+ Expression isGroupingUsed,
+ Expression maxIntegerDigits,
+ Expression minIntegerDigits,
+ Expression maxFractionDigits,
+ Expression minFractionDigits,
+ Expression locale) {
+ super(raw);
+ this.var = var;
+ this.value = value;
+ this.type = type;
+ this.pattern = pattern;
+ this.currencyCode = currencyCode;
+ this.currencySymbol = currencySymbol;
+ this.isGroupingUsed = isGroupingUsed;
+ this.maxIntegerDigits = maxIntegerDigits;
+ this.minIntegerDigits = minIntegerDigits;
+ this.maxFractionDigits = maxFractionDigits;
+ this.minFractionDigits = minFractionDigits;
+ this.locale = locale;
+ }
+
+ String format(JexlContext jexl, JXPathContext jxp)
+ throws Exception {
+ // Determine formatting locale
+ String var = getStringValue(this.var, jexl, jxp);
+ Number input = getNumberValue(this.value, jexl, jxp);
+ String type = getStringValue(this.type, jexl, jxp);
+ String pattern = getStringValue(this.pattern, jexl, jxp);
+ String currencyCode = getStringValue(this.currencyCode, jexl,
jxp);
+ String currencySymbol = getStringValue(this.currencySymbol,
+ jexl, jxp);
+ Boolean isGroupingUsed = getBooleanValue(this.isGroupingUsed,
+ jexl, jxp);
+ Number maxIntegerDigits = getNumberValue(this.maxIntegerDigits,
+ jexl, jxp);
+ Number minIntegerDigits = getNumberValue(this.minIntegerDigits,
+ jexl, jxp);
+ Number maxFractionDigits =
getNumberValue(this.maxFractionDigits,
+ jexl, jxp);
+ Number minFractionDigits =
getNumberValue(this.minFractionDigits,
+ jexl, jxp);
+ String localeStr = getStringValue(this.locale,
+ jexl, jxp);
+ Locale loc;
+ if (localeStr == null) {
+ loc = Locale.getDefault();
+ } else {
+ loc = parseLocale(localeStr, null);
+ }
+ String formatted;
+ if (loc != null) {
+ // Create formatter
+ NumberFormat formatter = null;
+ if ((pattern != null) && !pattern.equals("")) {
+ // if 'pattern' is specified, 'type' is ignored
+ DecimalFormatSymbols symbols = new
DecimalFormatSymbols(loc);
+ formatter = new DecimalFormat(pattern, symbols);
+ } else {
+ formatter = createFormatter(loc,
+ type);
+
+ }
+ if (((pattern != null) && !pattern.equals(""))
+ || CURRENCY.equalsIgnoreCase(type)) {
+ setCurrency(formatter,
+ currencyCode,
+ currencySymbol);
+ }
+ configureFormatter(formatter,
+ isGroupingUsed,
+ maxIntegerDigits,
+ minIntegerDigits,
+ maxFractionDigits,
+ minFractionDigits);
+ formatted = formatter.format(input);
+ } else {
+ // no formatting locale available, use toString()
+ formatted = input.toString();
+ }
+ if (var != null) {
+ jexl.getVars().put(var, formatted);
+ jxp.getVariables().declareVariable(var,
+ formatted);
+ return null;
+ }
+ return formatted;
+ }
+
+ private NumberFormat createFormatter(Locale loc,
+ String type)
+ throws Exception {
+ NumberFormat formatter = null;
+ if ((type == null) || NUMBER.equalsIgnoreCase(type)) {
+ formatter = NumberFormat.getNumberInstance(loc);
+ } else if (CURRENCY.equalsIgnoreCase(type)) {
+ formatter = NumberFormat.getCurrencyInstance(loc);
+ } else if (PERCENT.equalsIgnoreCase(type)) {
+ formatter = NumberFormat.getPercentInstance(loc);
+ } else {
+ throw new IllegalArgumentException("Invalid type: \"" + type
+ "\": should be \"number\" or \"currency\" or \"percent\"");
+ }
+ return formatter;
+ }
+
+ /*
+ * Applies the 'groupingUsed', 'maxIntegerDigits',
'minIntegerDigits',
+ * 'maxFractionDigits', and 'minFractionDigits' attributes to the
given
+ * formatter.
+ */
+ private void configureFormatter(NumberFormat formatter,
+ Boolean isGroupingUsed,
+ Number maxIntegerDigits,
+ Number minIntegerDigits,
+ Number maxFractionDigits,
+ Number minFractionDigits) {
+ if (isGroupingUsed != null)
+ formatter.setGroupingUsed(isGroupingUsed.booleanValue());
+ if (maxIntegerDigits != null)
+
formatter.setMaximumIntegerDigits(maxIntegerDigits.intValue());
+ if (minIntegerDigits != null)
+
formatter.setMinimumIntegerDigits(minIntegerDigits.intValue());
+ if (maxFractionDigits != null)
+
formatter.setMaximumFractionDigits(maxFractionDigits.intValue());
+ if (minFractionDigits != null)
+
formatter.setMinimumFractionDigits(minFractionDigits.intValue());
+ }
+
+ /*
+ * Override the formatting locale's default currency symbol with the
+ * specified currency code (specified via the "currencyCode"
attribute) or
+ * currency symbol (specified via the "currencySymbol" attribute).
+ *
+ * If both "currencyCode" and "currencySymbol" are present,
+ * "currencyCode" takes precedence over "currencySymbol" if the
+ * java.util.Currency class is defined in the container's runtime
(that
+ * is, if the container's runtime is J2SE 1.4 or greater), and
+ * "currencySymbol" takes precendence over "currencyCode" otherwise.
+ *
+ * If only "currencyCode" is given, it is used as a currency symbol
if
+ * java.util.Currency is not defined.
+ *
+ * Example:
+ *
+ * JDK "currencyCode" "currencySymbol" Currency symbol being
displayed
+ *
-----------------------------------------------------------------------
+ * all --- --- Locale's default currency
symbol
+ *
+ * <1.4 EUR --- EUR
+ * >=1.4 EUR --- Locale's currency symbol
for Euro
+ *
+ * all --- \u20AC \u20AC
+ *
+ * <1.4 EUR \u20AC \u20AC
+ * >=1.4 EUR \u20AC Locale's currency symbol
for Euro
+ */
+ private void setCurrency(NumberFormat formatter,
+ String currencyCode,
+ String currencySymbol) throws Exception {
+ String code = null;
+ String symbol = null;
+
+ if ((currencyCode == null) && (currencySymbol == null)) {
+ return;
+ }
+
+ if ((currencyCode != null) && (currencySymbol != null)) {
+ if (currencyClass != null)
+ code = currencyCode;
+ else
+ symbol = currencySymbol;
+ } else if (currencyCode == null) {
+ symbol = currencySymbol;
+ } else {
+ if (currencyClass != null)
+ code = currencyCode;
+ else
+ symbol = currencyCode;
+ }
+
+ if (code != null) {
+ Object[] methodArgs = new Object[1];
+
+ /*
+ * java.util.Currency.getInstance()
+ */
+ Method m = currencyClass.getMethod("getInstance",
+ new Class[]
{String.class});
+
+ methodArgs[0] = code;
+ Object currency = m.invoke(null, methodArgs);
+
+ /*
+ * java.text.NumberFormat.setCurrency()
+ */
+ Class[] paramTypes = new Class[1];
+ paramTypes[0] = currencyClass;
+ Class numberFormatClass =
Class.forName("java.text.NumberFormat");
+ m = numberFormatClass.getMethod("setCurrency", paramTypes);
+ methodArgs[0] = currency;
+ m.invoke(formatter, methodArgs);
+ } else {
+ /*
+ * Let potential ClassCastException propagate up (will almost
+ * never happen)
+ */
+ DecimalFormat df = (DecimalFormat) formatter;
+ DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
+ dfs.setCurrencySymbol(symbol);
+ df.setDecimalFormatSymbols(dfs);
+ }
+ }
+ }
+
+ // formatDate tag (borrows from Jakarta taglibs JSTL)
+
+ static class StartFormatDate extends StartInstruction {
+
+ private static final String DATE = "date";
+ private static final String TIME = "time";
+ private static final String DATETIME = "both";
+
+ Expression var;
+ Expression value;
+ Expression type;
+ Expression pattern;
+ Expression timeZone;
+ Expression dateStyle;
+ Expression timeStyle;
+ Expression locale;
+
+ StartFormatDate(StartElement raw,
+ Expression var,
+ Expression value,
+ Expression type,
+ Expression pattern,
+ Expression timeZone,
+ Expression dateStyle,
+ Expression timeStyle,
+ Expression locale) {
+ super(raw);
+ this.var = var;
+ this.value = value;
+ this.type = type;
+ this.pattern = pattern;
+ this.timeZone = timeZone;
+ this.dateStyle = dateStyle;
+ this.timeStyle = timeStyle;
+ this.locale = locale;
+ }
+
+ String format(JexlContext jexl, JXPathContext jxp)
+ throws Exception {
+ String var = getStringValue(this.var, jexl, jxp);
+ Object value = getValue(this.value, jexl, jxp);
+ Object locVal = getValue(this.locale,
+ jexl, jxp);
+ String pattern = getStringValue(this.pattern,
+ jexl, jxp);
+ Object timeZone = getValue(this.timeZone, jexl, jxp);
+
+ String type = getStringValue(this.type, jexl, jxp);
+ String timeStyle = getStringValue(this.timeStyle, jexl, jxp);
+ String dateStyle = getStringValue(this.dateStyle, jexl, jxp);
+
+ String formatted = null;
+
+ // Create formatter
+ Locale locale;
+ if (locVal != null) {
+ if (locVal instanceof Locale) {
+ locale = (Locale)locVal;
+ } else {
+ locale = parseLocale(locVal.toString(), null);
+ }
+ } else {
+ locale = Locale.getDefault();
+ }
+ DateFormat formatter = createFormatter(locale,
+ type,
+ dateStyle,
+ timeStyle);
+ // Apply pattern, if present
+ if (pattern != null) {
+ try {
+ ((SimpleDateFormat) formatter).applyPattern(pattern);
+ } catch (ClassCastException cce) {
+ formatter = new SimpleDateFormat(pattern, locale);
+ }
+ }
+ // Set time zone
+ TimeZone tz = null;
+ if ((timeZone instanceof String)
+ && ((String) timeZone).equals("")) {
+ timeZone = null;
+ }
+ if (timeZone != null) {
+ if (timeZone instanceof String) {
+ tz = TimeZone.getTimeZone((String) timeZone);
+ } else if (timeZone instanceof TimeZone) {
+ tz = (TimeZone) timeZone;
+ } else {
+ throw new IllegalArgumentException("Illegal timeZone
value: \""+timeZone+"\"");
+ }
+ }
+ if (tz != null) {
+ formatter.setTimeZone(tz);
+ }
+ formatted = formatter.format(value);
+ if (var != null) {
+ jexl.getVars().put(var, formatted);
+ jxp.getVariables().declareVariable(var,
+ formatted);
+ return null;
+ }
+ return formatted;
+ }
+
+ private DateFormat createFormatter(Locale loc,
+ String type,
+ String dateStyle,
+ String timeStyle)
+ throws Exception {
+ DateFormat formatter = null;
+ if ((type == null) || DATE.equalsIgnoreCase(type)) {
+ formatter = DateFormat.getDateInstance(getStyle(dateStyle),
+ loc);
+ } else if (TIME.equalsIgnoreCase(type)) {
+ formatter = DateFormat.getTimeInstance(getStyle(timeStyle),
+ loc);
+ } else if (DATETIME.equalsIgnoreCase(type)) {
+ formatter =
DateFormat.getDateTimeInstance(getStyle(dateStyle),
+
getStyle(timeStyle),
+ loc);
+ } else {
+ throw new IllegalArgumentException("Invalid type: \""+
type+"\"");
+ }
+ return formatter;
+ }
+
+ private static final String DEFAULT = "default";
+ private static final String SHORT = "short";
+ private static final String MEDIUM = "medium";
+ private static final String LONG = "long";
+ private static final String FULL = "full";
+
+ private int getStyle(String style) {
+ int ret = DateFormat.DEFAULT;
+ if (style != null) {
+ if (DEFAULT.equalsIgnoreCase(style)) {
+ ret = DateFormat.DEFAULT;
+ } else if (SHORT.equalsIgnoreCase(style)) {
+ ret = DateFormat.SHORT;
+ } else if (MEDIUM.equalsIgnoreCase(style)) {
+ ret = DateFormat.MEDIUM;
+ } else if (LONG.equalsIgnoreCase(style)) {
+ ret = DateFormat.LONG;
+ } else if (FULL.equalsIgnoreCase(style)) {
+ ret = DateFormat.FULL;
+ } else {
+ throw new IllegalArgumentException("Invalid style: \"" +
style +"\": should be \"default\" or \"short\" or \"medium\" or \"long\" or
\"full\"");
+ }
+ }
+ return ret;
+ }
+ }
+
+
static class Parser implements ContentHandler, LexicalHandler {
StartDocument startEvent;
@@ -1677,6 +2222,94 @@
new StartForEach(startElement, expr,
var, begin, end, step);
newEvent = startForEach;
+ } else if (localName.equals(FORMAT_NUMBER)) {
+ Expression value =
+ compileExpr(attrs.getValue("value"),
+ null, locator);;
+ Expression type =
+ compileExpr(attrs.getValue("type"),
+ null, locator);
+ Expression pattern =
+ compileExpr(attrs.getValue("pattern"),
+ null, locator);
+ Expression currencyCode =
+ compileExpr(attrs.getValue("currencyCode"),
+ null, locator);;
+ Expression currencySymbol =
+ compileExpr(attrs.getValue("currencySymbol"),
+ null, locator);;
+ Expression isGroupingUsed =
+ compileBoolean(attrs.getValue("isGroupingUsed"),
+ null, locator);
+ Expression maxIntegerDigits =
+ compileInt(attrs.getValue("maxIntegerDigits"),
+ null, locator);
+ Expression minIntegerDigits =
+ compileInt(attrs.getValue("minIntegerDigits"),
+ null, locator);
+ Expression maxFractionDigits =
+ compileInt(attrs.getValue("maxFractionDigits"),
+ null, locator);
+ Expression minFractionDigits =
+ compileInt(attrs.getValue("minFractionDigits"),
+ null, locator);
+ Expression var =
+ compileExpr(attrs.getValue("var"),
+ null, locator);
+ Expression locale =
+ compileExpr(attrs.getValue("locale"),
+ null, locator);
+ StartFormatNumber startFormatNumber =
+ new StartFormatNumber(startElement,
+ var,
+ value,
+ type,
+ pattern,
+ currencyCode,
+ currencySymbol,
+ isGroupingUsed,
+ maxIntegerDigits,
+ minIntegerDigits,
+ maxFractionDigits,
+ minFractionDigits,
+ locale);
+ newEvent = startFormatNumber;
+ } else if (localName.equals(FORMAT_DATE)) {
+ Expression var =
+ compileExpr(attrs.getValue("var"),
+ null, locator);
+ Expression value =
+ compileExpr(attrs.getValue("value"),
+ null, locator);;
+ Expression type =
+ compileExpr(attrs.getValue("type"),
+ null, locator);
+ Expression pattern =
+ compileExpr(attrs.getValue("pattern"),
+ null, locator);
+ Expression timeZone =
+ compileExpr(attrs.getValue("timeZone"),
+ null, locator);
+ Expression dateStyle =
+ compileExpr(attrs.getValue("dateStyle"),
+ null, locator);
+ Expression timeStyle =
+ compileExpr(attrs.getValue("timeStyle"),
+ null, locator);
+ Expression locale =
+ compileExpr(attrs.getValue("locale"),
+ null, locator);
+ StartFormatDate startFormatDate =
+ new StartFormatDate(startElement,
+ var,
+ value,
+ type,
+ pattern,
+ timeZone,
+ dateStyle,
+ timeStyle,
+ locale);
+ newEvent = startFormatDate;
} else if (localName.equals(CHOOSE)) {
StartChoose startChoose = new StartChoose(startElement);
newEvent = startChoose;
@@ -2705,8 +3338,8 @@
} catch (Exception e) {
throw new
SAXParseException(e.getMessage(),
ev.location,
- e);
- }
+ e);
+ }
if (val == null) {
val = "";
}
@@ -2725,6 +3358,38 @@
startElement.raw,
attrs);
+ } else if (ev instanceof StartFormatNumber) {
+ StartFormatNumber startFormatNumber =
+ (StartFormatNumber)ev;
+ try {
+ String result =
+ startFormatNumber.format(jexlContext,
+ jxpathContext);
+ if (result != null) {
+ char[] chars = result.toCharArray();
+ consumer.characters(chars, 0, chars.length);
+ }
+ } catch (Exception e) {
+ throw new SAXParseException(e.getMessage(),
+ ev.location,
+ e);
+ }
+ } else if (ev instanceof StartFormatDate) {
+ StartFormatDate startFormatDate =
+ (StartFormatDate)ev;
+ try {
+ String result =
+ startFormatDate.format(jexlContext,
+ jxpathContext);
+ if (result != null) {
+ char[] chars = result.toCharArray();
+ consumer.characters(chars, 0, chars.length);
+ }
+ } catch (Exception e) {
+ throw new SAXParseException(e.getMessage(),
+ ev.location,
+ e);
+ }
} else if (ev instanceof StartPrefixMapping) {
StartPrefixMapping startPrefixMapping =
(StartPrefixMapping)ev;