Revision: 673
http://stripes.svn.sourceforge.net/stripes/?rev=673&view=rev
Author: tfenne
Date: 2007-12-12 04:52:50 -0800 (Wed, 12 Dec 2007)
Log Message:
-----------
Various changes to the DateTypeConverter based of a patch by Freddy Daoud.
Provides fixes for STS-445, STS-443 and STS-441.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/validation/DateTypeConverter.java
trunk/tests/src/net/sourceforge/stripes/validation/DateTypeConverterTest.java
Modified:
trunk/stripes/src/net/sourceforge/stripes/validation/DateTypeConverter.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/validation/DateTypeConverter.java
2007-12-12 12:46:27 UTC (rev 672)
+++ trunk/stripes/src/net/sourceforge/stripes/validation/DateTypeConverter.java
2007-12-12 12:52:50 UTC (rev 673)
@@ -14,6 +14,8 @@
*/
package net.sourceforge.stripes.validation;
+import net.sourceforge.stripes.controller.StripesFilter;
+
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -21,12 +23,10 @@
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
import java.util.regex.Pattern;
-import net.sourceforge.stripes.validation.ScopedLocalizableError;
-import net.sourceforge.stripes.validation.TypeConverter;
-import net.sourceforge.stripes.validation.ValidationError;
-
/**
* <p>A TypeConverter that aggressively attempts to convert a String to a
java.util.Date object.
* Under the covers it uses DateFormat instances to do the heavy lifting, but
since
@@ -44,16 +44,11 @@
* DateFormat succeeds and returns a Date, that Date will be returned as the
result of the
* conversion. If all DateFormats fail, a validation error will be
produced.</p>
*
- * <p>DateTypeConverter is designed to be overridden in order to change its
behaviour. Subclasses can
- * override the preProcessInput() method to change the pre-processing behavior
if desired. Similarly,
- * subclasses can override getFormatStrings() to change the set of format
strings used in parsing,
- * or even override getDateFormats() to change how the DateFormat objects get
constructed.</p>
+ * <p>The set of formats is obtained from getFormatStrings(). The default set
of formats used is
+ * constructed by taking the default SHORT, MEDIUM and LONG formats for the
input locale (and
+ * replacing all non-space separator characters with spaces), and adding
formats to obtain the
+ * following patterns:</p>
*
- * <p>The set of formats used is constructed by taking the default SHORT,
MEDIUM and LONG formats
- * for the input locale (and replacing all non-space separator characters with
spaces), supplied by
- * getDefaultLocalizedFormats(), and adding formats based on the format
strings supplied by getFormatStrings().
- * This results in a list of DateFormats with the following patterns:</p>
- *
* <ul>
* <li>SHORT (e.g. 'M d yy' for Locale.US)</li>
* <li>MEDIUM (e.g. 'MMM d yyyy' for Locale.US)</li>
@@ -64,6 +59,27 @@
* <li>EEE MMM dd HH:mm:ss zzz yyyy (the format created by
Date.toString())</li>
* </ul>
* </p>
+ *
+ * <p>This default set of formats can be changed by providing a different set
of format strings in
+ * the Stripes resouce bundle, or by subclassing and overriding
getFormatStrings(). In all cases
+ * patterns should be specified using single spaces as separators instead of
slashes, dashes
+ * or other characaters.</p>
+ *
+ * <p>The regular expression pattern used in the pre-process method can also
be changed in the
+ * Stripes resource bundle, or by subclassing and overriding the
getPreProcessPattern() method.</p>
+ *
+ * <p>The keys used in the resource bundle to specify the format strings and
the pre-process
+ * pattern are:
+ * <ul>
+ * <li>stripes.dateTypeConverter.formatStrings</li>
+ * <li>stripes.dateTypeConverter.preProcessPattern</li>
+ * </ul>
+ * </p>
+ *
+ * <p>DateTypeConverter can also be overridden in order to change its
behaviour. Subclasses can
+ * override the preProcessInput() method to change the pre-processing behavior
if desired. Similarly,
+ * subclasses can override getDateFormats() to change how the DateFormat
objects get constructed.
+ * </p>
*/
public class DateTypeConverter implements TypeConverter<Date> {
private Locale locale;
@@ -82,26 +98,36 @@
* @return the current input locale.
*/
public Locale getLocale() {
- return locale;
+ return locale;
}
-
+
/**
- * A pattern used to pre-process Strings before the parsing attempt is
made. Since
+ * <p>A pattern used to pre-process Strings before the parsing attempt is
made. Since
* SimpleDateFormat strictly enforces that the separator characters in the
input are the same
* as those in the pattern, this regular expression is used to remove
commas, slashes, hyphens
* and periods from the input String (replacing them with spaces) and to
collapse multiple
- * white-space characters into a single space.
+ * white-space characters into a single space.</p>
+ *
+ * <p>This pattern can be changed by providing a different value under the
+ * <code>'stripes.dateTypeConverter.preProcessPattern'</code> key in the
resouce
+ * bundle. The default value is <code>(?<!GMT)[\\s,-/\\.]+</code>.</p>
*/
- public static final Pattern pattern =
Pattern.compile("(?<!GMT)[\\s,-/\\.]+");
+ public static final Pattern PRE_PROCESS_PATTERN =
Pattern.compile("(?<!GMT)[\\s,-/\\.]+");
/** The default set of date patterns used to parse dates with
SimpleDateFormat. */
public static final String[] formatStrings = new String[] {
- "d MMM yy",
- "yyyy M d",
- "yyyy MMM d",
- "EEE MMM dd HH:mm:ss zzz yyyy"
+ "d MMM yy",
+ "yyyy M d",
+ "yyyy MMM d",
+ "EEE MMM dd HH:mm:ss zzz yyyy"
};
-
+
+ /** The key used to look up the localized format strings from the resource
bundle. */
+ public static final String KEY_FORMAT_STRINGS =
"stripes.dateTypeConverter.formatStrings";
+
+ /** The key used to look up the pre-process pattern from the resource
bundle. */
+ public static final String KEY_PRE_PROCESS_PATTERN =
"stripes.dateTypeConverter.preProcessPattern";
+
/**
* Returns an array of format strings that will be used, in order, to try
and parse the date.
* This method can be overridden to make DateTypeConverter use a different
set of format
@@ -109,54 +135,43 @@
* patterns should be expressed with spaces as separators, not slashes,
hyphens etc.
*/
protected String[] getFormatStrings() {
- return DateTypeConverter.formatStrings;
- }
-
- /**
- * Returns an array of 3 DateFormats which are based upon the standard
SHORT, MEDIUM and LONG
- * formats for the input locale, with all separator characters replaced
with spaces.
- */
- protected SimpleDateFormat[] getDefaultLocalizedFormats() {
- SimpleDateFormat[] dateFormats = new SimpleDateFormat[3];
-
- dateFormats[0] = (SimpleDateFormat)
DateFormat.getDateInstance(DateFormat.SHORT, locale);
- dateFormats[0].applyPattern(
preProcessInput(dateFormats[0].toPattern()) );
+ try {
+ return getResourceString(KEY_FORMAT_STRINGS).split(", *");
+ }
+ catch (MissingResourceException mre) {
+ // First get the locale specific date format patterns
+ int[] dateFormats = { DateFormat.SHORT, DateFormat.MEDIUM,
DateFormat.LONG };
+ String[] formatStrings = new String[dateFormats.length +
DateTypeConverter.formatStrings.length];
- dateFormats[1] = (SimpleDateFormat)
DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
- dateFormats[1].applyPattern(
preProcessInput(dateFormats[1].toPattern()) );
+ for (int i=0; i<dateFormats.length; i++) {
+ SimpleDateFormat dateFormat = (SimpleDateFormat)
DateFormat.getDateInstance(dateFormats[i], locale);
+ formatStrings[i] = preProcessInput(dateFormat.toPattern());
+ }
- dateFormats[2] = (SimpleDateFormat)
DateFormat.getDateInstance(DateFormat.LONG, locale);
- dateFormats[2].applyPattern(
preProcessInput(dateFormats[2].toPattern()) );
-
- return dateFormats;
+ // Then copy the default format strings over too
+ System.arraycopy(DateTypeConverter.formatStrings, 0,
+ formatStrings, dateFormats.length,
+ DateTypeConverter.formatStrings.length);
+
+ return formatStrings;
+ }
}
-
/**
* Returns an array of DateFormat objects that will be used in sequence to
try and parse the
* date String. This method will be called once when the DateTypeConverter
instance is
- * initialized. It returns an array that combines the dateformats returned
by
- * getDefaultLocalizedFormats() and getFormatStrings(), in that order.
+ * initialized. It first calls getFormatStrings() to obtain the format
strings that are used to
+ * construct SimpleDateFormat instances.
*/
protected DateFormat[] getDateFormats() {
String[] formatStrings = getFormatStrings();
- SimpleDateFormat[] defaultLocalizedFormats =
getDefaultLocalizedFormats();
-
- SimpleDateFormat[] dateFormats =
- new SimpleDateFormat[defaultLocalizedFormats.length +
formatStrings.length];
+ SimpleDateFormat[] dateFormats = new
SimpleDateFormat[formatStrings.length];
- // Put the default localized dateformats in the aggregated array
- for (int i=0; i<defaultLocalizedFormats.length; i++) {
- dateFormats[i] = defaultLocalizedFormats[i];
+ for (int i=0; i<formatStrings.length; i++) {
+ dateFormats[i] = new SimpleDateFormat(formatStrings[i], locale);
dateFormats[i].setLenient(false);
}
-
- // Create the dateFormats from the format Strings
- for (int i=0; i<formatStrings.length; i++) {
- dateFormats[i+defaultLocalizedFormats.length] = new
SimpleDateFormat(formatStrings[i], locale);
- dateFormats[i+defaultLocalizedFormats.length].setLenient(false);
- }
-
+
return dateFormats;
}
@@ -194,15 +209,42 @@
}
/**
+ * Returns the regular expression pattern used in the pre-process method.
Looks for a pattern in
+ * the resource bundle under the key
'stripes.dateTypeConverter.preProcessPattern'. If no value
+ * is found, the pattern <code>(?<!GMT)[\\s,-/\\.]+</code> is used by
default. The pattern is
+ * used by preProcessInput() to replace all matches by single spaces.
+ */
+ protected Pattern getPreProcessPattern() {
+ try {
+ return Pattern.compile(getResourceString(KEY_PRE_PROCESS_PATTERN));
+ }
+ catch (MissingResourceException exc) {
+ return DateTypeConverter.PRE_PROCESS_PATTERN;
+ }
+ }
+
+ /**
* Pre-processes the input String to improve the chances of parsing it.
First uses the regular
- * expression Pattern stored on this class to remove all separator chars
and ensure that
- * components are separated by single spaces. Then, if the string
contains only two components
- * the current year is appended to ensure that dates like "12/25" parse to
the current year
- * instead of failing altogether.
+ * expression Pattern returned by getPreProcessPattern() to remove all
separator chars and
+ * ensure that components are separated by single spaces. Then invokes
+ * [EMAIL PROTECTED] #checkAndAppendYear(String)} to append the year to
the date in case the date
+ * is in a format like "12/25" which would otherwise fail to parse.
*/
protected String preProcessInput(String input) {
- input = DateTypeConverter.pattern.matcher(input.trim()).replaceAll("
");
+ input = getPreProcessPattern().matcher(input.trim()).replaceAll(" ");
+ input = checkAndAppendYear(input);
+ return input;
+ }
+ /**
+ * Checks to see how many 'parts' there are to the date (separated by
spaces) and
+ * if there are only two parts it adds the current year to the end by
geting the
+ * Locale specific year string from a Calendar instance.
+ *
+ * @param input the date string after the preprocess pattern has been run
against it
+ * @return either the date string as is, or with the year appened to the
end
+ */
+ protected String checkAndAppendYear(String input) {
// Count the spaces, date components = spaces + 1
int count = 0;
for (char ch : input.toCharArray()) {
@@ -213,7 +255,13 @@
if (count == 1) {
input += " " + Calendar.getInstance(locale).get(Calendar.YEAR);
}
-
return input;
}
-}
+
+ /** Convenience method to fetch a property from the resource bundle. */
+ protected String getResourceString(String key) throws
MissingResourceException {
+ return StripesFilter.getConfiguration().getLocalizationBundleFactory()
+ .getErrorMessageBundle(locale).getString(key);
+
+ }
+}
\ No newline at end of file
Modified:
trunk/tests/src/net/sourceforge/stripes/validation/DateTypeConverterTest.java
===================================================================
---
trunk/tests/src/net/sourceforge/stripes/validation/DateTypeConverterTest.java
2007-12-12 12:46:27 UTC (rev 672)
+++
trunk/tests/src/net/sourceforge/stripes/validation/DateTypeConverterTest.java
2007-12-12 12:52:50 UTC (rev 673)
@@ -8,6 +8,7 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.Calendar;
+import java.util.MissingResourceException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -22,7 +23,13 @@
private DateFormat format = new SimpleDateFormat("MM/dd/yyyy", Locale.US);
private DateTypeConverter getConverter(Locale locale) {
- DateTypeConverter converter = new DateTypeConverter();
+ DateTypeConverter converter = new DateTypeConverter(){
+ @Override
+ protected String getResourceString(final String key) throws
MissingResourceException {
+ throw new MissingResourceException("Bundle not available to
unit tests.", "", key);
+ }
+ };
+
converter.setLocale(locale);
return converter;
}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
SF.Net email is sponsored by:
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development