You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2007/10/30 22:35:12 UTC
svn commit: r590474 - in /commons/proper/configuration/trunk/src:
java/org/apache/commons/configuration/plist/
test/org/apache/commons/configuration/plist/
Author: oheger
Date: Tue Oct 30 14:35:11 2007
New Revision: 590474
URL: http://svn.apache.org/viewvc?rev=590474&view=rev
Log:
CONFIGURATION-261: Parsing and formatting date values for PropertyListConfiguration is now done manually and works on Java 1.3, too
Modified:
commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListConfiguration.java
commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.java
commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.jj
commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListConfiguration.java
commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListParser.java
Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListConfiguration.java?rev=590474&r1=590473&r2=590474&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListConfiguration.java Tue Oct 30 14:35:11 2007
@@ -23,12 +23,12 @@
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Date;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.util.TimeZone;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.configuration.AbstractHierarchicalFileConfiguration;
@@ -82,15 +82,51 @@
*/
public class PropertyListConfiguration extends AbstractHierarchicalFileConfiguration
{
- /** The format used for the date objects in the plist files. */
- static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+ /** Constant for the separator parser for the date part. */
+ private static final DateComponentParser DATE_SEPARATOR_PARSER = new DateSeparatorParser(
+ "-");
+
+ /** Constant for the separator parser for the time part. */
+ private static final DateComponentParser TIME_SEPARATOR_PARSER = new DateSeparatorParser(
+ ":");
+
+ /** Constant for the separator parser for blanks between the parts. */
+ private static final DateComponentParser BLANK_SEPARATOR_PARSER = new DateSeparatorParser(
+ " ");
+
+ /** An array with the component parsers for dealing with dates. */
+ private static final DateComponentParser[] DATE_PARSERS =
+ {new DateSeparatorParser("<*D"), new DateFieldParser(Calendar.YEAR, 4),
+ DATE_SEPARATOR_PARSER, new DateFieldParser(Calendar.MONTH, 2, 1),
+ DATE_SEPARATOR_PARSER, new DateFieldParser(Calendar.DATE, 2),
+ BLANK_SEPARATOR_PARSER,
+ new DateFieldParser(Calendar.HOUR_OF_DAY, 2),
+ TIME_SEPARATOR_PARSER, new DateFieldParser(Calendar.MINUTE, 2),
+ TIME_SEPARATOR_PARSER, new DateFieldParser(Calendar.SECOND, 2),
+ BLANK_SEPARATOR_PARSER, new DateTimeZoneParser(),
+ new DateSeparatorParser(">")};
+
+ /** Constant for the ID prefix for GMT time zones. */
+ private static final String TIME_ZONE_PREFIX = "GMT";
/** The serial version UID. */
private static final long serialVersionUID = 3227248503779092127L;
+ /** Constant for the milliseconds of a minute.*/
+ private static final int MILLIS_PER_MINUTE = 1000 * 60;
+
+ /** Constant for the minutes per hour.*/
+ private static final int MINUTES_PER_HOUR = 60;
+
/** Size of the indentation for the generated file. */
private static final int INDENT_SIZE = 4;
+ /** Constant for the length of a time zone.*/
+ private static final int TIME_ZONE_LENGTH = 5;
+
+ /** Constant for the padding character in the date format.*/
+ private static final char PAD_CHAR = '0';
+
/**
* Creates an empty PropertyListConfiguration object which can be
* used to synthesize a new plist file by adding values and
@@ -332,7 +368,7 @@
}
else if (value instanceof Date)
{
- out.print("<*D" + DATE_FORMAT.format((Date) value) + ">");
+ out.print(formatDate((Date) value));
}
else if (value != null)
{
@@ -382,5 +418,265 @@
}
return s;
+ }
+
+ /**
+ * Parses a date in a format like
+ * <code><*D2002-03-22 11:30:00 +0100></code>.
+ *
+ * @param s the string with the date to be parsed
+ * @return the parsed date
+ * @throws ParseException if an error occurred while parsing the string
+ */
+ static Date parseDate(String s) throws ParseException
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+ int index = 0;
+
+ for (int i = 0; i < DATE_PARSERS.length; i++)
+ {
+ index += DATE_PARSERS[i].parseComponent(s, index, cal);
+ }
+
+ return cal.getTime();
+ }
+
+ /**
+ * Returns a string representation for the date specified by the given
+ * calendar.
+ *
+ * @param cal the calendar with the initialized date
+ * @return a string for this date
+ */
+ static String formatDate(Calendar cal)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ for (int i = 0; i < DATE_PARSERS.length; i++)
+ {
+ DATE_PARSERS[i].formatComponent(buf, cal);
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Returns a string representation for the specified date.
+ *
+ * @param date the date
+ * @return a string for this date
+ */
+ static String formatDate(Date date)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return formatDate(cal);
+ }
+
+ /**
+ * A helper class for parsing and formatting date literals. Usually we would
+ * use <code>SimpleDateFormat</code> for this purpose, but in Java 1.3 the
+ * functionality of this class is limited. So we have a hierarchy of parser
+ * classes instead that deal with the different components of a date
+ * literal.
+ */
+ private abstract static class DateComponentParser
+ {
+ /**
+ * Parses a component from the given input string.
+ *
+ * @param s the string to be parsed
+ * @param index the current parsing position
+ * @param cal the calendar where to store the result
+ * @return the length of the processed component
+ * @throws ParseException if the component cannot be extracted
+ */
+ public abstract int parseComponent(String s, int index, Calendar cal)
+ throws ParseException;
+
+ /**
+ * Formats a date component. This method is used for converting a date
+ * in its internal representation into a string literal.
+ *
+ * @param buf the target buffer
+ * @param cal the calendar with the current date
+ */
+ public abstract void formatComponent(StringBuffer buf, Calendar cal);
+
+ /**
+ * Checks whether the given string has at least <code>length</code>
+ * characters starting from the given parsing position. If this is not
+ * the case, an exception will be thrown.
+ *
+ * @param s the string to be tested
+ * @param index the current index
+ * @param length the minimum length after the index
+ * @throws ParseException if the string is too short
+ */
+ protected void checkLength(String s, int index, int length)
+ throws ParseException
+ {
+ int len = (s == null) ? 0 : s.length();
+ if (index + length > len)
+ {
+ throw new ParseException("Input string too short: " + s
+ + ", index: " + index);
+ }
+ }
+
+ /**
+ * Adds a number to the given string buffer and adds leading '0'
+ * characters until the given length is reached.
+ *
+ * @param buf the target buffer
+ * @param num the number to add
+ * @param length the required length
+ */
+ protected void padNum(StringBuffer buf, int num, int length)
+ {
+ buf.append(StringUtils.leftPad(String.valueOf(num), length,
+ PAD_CHAR));
+ }
+ }
+
+ /**
+ * A specialized date component parser implementation that deals with
+ * numeric calendar fields. The class is able to extract fields from a
+ * string literal and to format a literal from a calendar.
+ */
+ private static class DateFieldParser extends DateComponentParser
+ {
+ /** Stores the calendar field to be processed. */
+ private int calendarField;
+
+ /** Stores the length of this field. */
+ private int length;
+
+ /** An optional offset to add to the calendar field. */
+ private int offset;
+
+ /**
+ * Creates a new instance of <code>DateFieldParser</code>.
+ *
+ * @param calFld the calendar field code
+ * @param len the length of this field
+ */
+ public DateFieldParser(int calFld, int len)
+ {
+ this(calFld, len, 0);
+ }
+
+ /**
+ * Creates a new instance of <code>DateFieldParser</code> and fully
+ * initializes it.
+ *
+ * @param calFld the calendar field code
+ * @param len the length of this field
+ * @param ofs an offset to add to the calendar field
+ */
+ public DateFieldParser(int calFld, int len, int ofs)
+ {
+ calendarField = calFld;
+ length = len;
+ offset = ofs;
+ }
+
+ public void formatComponent(StringBuffer buf, Calendar cal)
+ {
+ padNum(buf, cal.get(calendarField) + offset, length);
+ }
+
+ public int parseComponent(String s, int index, Calendar cal)
+ throws ParseException
+ {
+ checkLength(s, index, length);
+ try
+ {
+ cal.set(calendarField, Integer.parseInt(s.substring(index,
+ index + length))
+ - offset);
+ return length;
+ }
+ catch (NumberFormatException nfex)
+ {
+ throw new ParseException("Invalid number: " + s + ", index "
+ + index);
+ }
+ }
+ }
+
+ /**
+ * A specialized date component parser implementation that deals with
+ * separator characters.
+ */
+ private static class DateSeparatorParser extends DateComponentParser
+ {
+ /** Stores the separator. */
+ private String separator;
+
+ /**
+ * Creates a new instance of <code>DateSeparatorParser</code> and sets
+ * the separator string.
+ *
+ * @param sep the separator string
+ */
+ public DateSeparatorParser(String sep)
+ {
+ separator = sep;
+ }
+
+ public void formatComponent(StringBuffer buf, Calendar cal)
+ {
+ buf.append(separator);
+ }
+
+ public int parseComponent(String s, int index, Calendar cal)
+ throws ParseException
+ {
+ checkLength(s, index, separator.length());
+ if (!s.startsWith(separator, index))
+ {
+ throw new ParseException("Invalid input: " + s + ", index "
+ + index + ", expected " + separator);
+ }
+ return separator.length();
+ }
+ }
+
+ /**
+ * A specialized date component parser implementation that deals with the
+ * time zone part of a date component.
+ */
+ private static class DateTimeZoneParser extends DateComponentParser
+ {
+ public void formatComponent(StringBuffer buf, Calendar cal)
+ {
+ TimeZone tz = cal.getTimeZone();
+ int ofs = tz.getRawOffset() / MILLIS_PER_MINUTE;
+ if (ofs < 0)
+ {
+ buf.append('-');
+ ofs = -ofs;
+ }
+ else
+ {
+ buf.append('+');
+ }
+ int hour = ofs / MINUTES_PER_HOUR;
+ int min = ofs % MINUTES_PER_HOUR;
+ padNum(buf, hour, 2);
+ padNum(buf, min, 2);
+ }
+
+ public int parseComponent(String s, int index, Calendar cal)
+ throws ParseException
+ {
+ checkLength(s, index, TIME_ZONE_LENGTH);
+ TimeZone tz = TimeZone.getTimeZone(TIME_ZONE_PREFIX
+ + s.substring(index, index + TIME_ZONE_LENGTH));
+ cal.setTimeZone(tz);
+ return TIME_ZONE_LENGTH;
+ }
}
}
Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.java?rev=590474&r1=590473&r2=590474&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.java Tue Oct 30 14:35:11 2007
@@ -84,17 +84,7 @@
*/
protected Date parseDate(String s) throws ParseException
{
- // remove the prefix "<*D" and the suffix ">"
- String substring = s.substring(3, s.length() - 1);
-
- try
- {
- return PropertyListConfiguration.DATE_FORMAT.parse(substring);
- }
- catch (Exception e)
- {
- throw (ParseException) new ParseException("Unable to parse the date '" + s + "' : " + e.getMessage());
- }
+ return PropertyListConfiguration.parseDate(s);
}
final public PropertyListConfiguration parse() throws ParseException {
Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.jj
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.jj?rev=590474&r1=590473&r2=590474&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.jj (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/plist/PropertyListParser.jj Tue Oct 30 14:35:11 2007
@@ -107,17 +107,7 @@
*/
protected Date parseDate(String s) throws ParseException
{
- // remove the prefix "<*D" and the suffix ">"
- String substring = s.substring(3, s.length() - 1);
-
- try
- {
- return PropertyListConfiguration.DATE_FORMAT.parse(substring);
- }
- catch (Exception e)
- {
- throw (ParseException) new ParseException("Unable to parse the date '" + s + "' : " + e.getMessage());
- }
+ return PropertyListConfiguration.parseDate(s);
}
}
Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListConfiguration.java?rev=590474&r1=590473&r2=590474&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListConfiguration.java Tue Oct 30 14:35:11 2007
@@ -19,9 +19,11 @@
import java.io.File;
import java.io.StringReader;
+import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Date;
+import java.util.TimeZone;
import junit.framework.TestCase;
import junitx.framework.ArrayAssert;
@@ -177,9 +179,13 @@
public void testDate() throws Exception
{
- Date date = PropertyListConfiguration.DATE_FORMAT.parse("2002-03-22 11:30:00 +0100");
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(2002, 2, 22, 11, 30, 0);
+ cal.setTimeZone(TimeZone.getTimeZone("GMT+0100"));
+ Date date = cal.getTime();
- assertEquals("date", date, config.getProperty("date"));
+ assertEquals("date", date, config.getProperty("date"));
}
public void testSave() throws Exception
@@ -296,5 +302,89 @@
{
PropertyListConfiguration copy = new PropertyListConfiguration(config);
assertFalse("Nothing was copied", copy.isEmpty());
+ }
+
+ /**
+ * Tests parsing a date with an invalid numeric value.
+ */
+ public void testParseDateNoNumber()
+ {
+ try
+ {
+ PropertyListConfiguration
+ .parseDate("<*D2002-03-22 1c:30:00 +0100>");
+ fail("Could parse date with an invalid number!");
+ }
+ catch (ParseException pex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests parsing a date that is not long enough.
+ */
+ public void testParseDateTooShort()
+ {
+ try
+ {
+ PropertyListConfiguration.parseDate("<*D2002-03-22 11:3>");
+ fail("Could parse too short date!");
+ }
+ catch (ParseException pex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests parsing a date that contains an invalid separator character.
+ */
+ public void testParseDateInvalidChar()
+ {
+ try
+ {
+ PropertyListConfiguration
+ .parseDate("<*D2002+03-22 11:30:00 +0100>");
+ fail("Could parse date with an invalid separator!");
+ }
+ catch (ParseException pex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tries parsing a null date. This should cause an exception.n
+ */
+ public void testParseDateNull()
+ {
+ try
+ {
+ PropertyListConfiguration.parseDate(null);
+ fail("Could parse null date!");
+ }
+ catch (ParseException pex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests formatting a date.
+ */
+ public void testFormatDate()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(2007, 9, 29, 23, 4, 30);
+ cal.setTimeZone(TimeZone.getTimeZone("GMT-0230"));
+ assertEquals("Wrong date literal (1)", "<*D2007-10-29 23:04:30 -0230>",
+ PropertyListConfiguration.formatDate(cal));
+ cal.clear();
+ cal.set(2007, 9, 30, 22, 2, 15);
+ cal.setTimeZone(TimeZone.getTimeZone("GMT+1111"));
+ assertEquals("Wrong date literal (2)", "<*D2007-10-30 22:02:15 +1111>",
+ PropertyListConfiguration.formatDate(cal));
}
}
Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListParser.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListParser.java?rev=590474&r1=590473&r2=590474&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListParser.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/plist/TestPropertyListParser.java Tue Oct 30 14:35:11 2007
@@ -19,7 +19,6 @@
import java.io.Reader;
import java.util.Calendar;
-import java.util.TimeZone;
import java.util.SimpleTimeZone;
import junit.framework.TestCase;