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;