Author: jahewson
Date: Thu Feb 13 03:35:20 2014
New Revision: 1567840

URL: http://svn.apache.org/r1567840
Log:
PDFBOX-1803 Refactoring of pdfbox.util.DateConverter

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
    
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/DateConverter.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
URL: 
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java?rev=1567840&r1=1567839&r2=1567840&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java 
(original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java 
Thu Feb 13 03:35:20 2014
@@ -770,13 +770,12 @@ public class COSDictionary extends COSBa
        /**
         * This is a convenience method that will get the dictionary object that
         * is expected to be a name and convert it to a string.  Null is 
returned
-        * if the entry does not exist in the dictionary.
+        * if the entry does not exist in the dictionary or if the date was 
invalid.
         *
         * @param key The key to the item in the dictionary.
-        * @return The name converted to a string.
-        * @throws IOException If there is an error converting to a date.
+        * @return The name converted to a date.
         */
-       public Calendar getDate( String key ) throws IOException
+       public Calendar getDate( String key )
        {
                return getDate( COSName.getPDFName( key ) );
        }
@@ -784,14 +783,12 @@ public class COSDictionary extends COSBa
        /**
         * This is a convenience method that will get the dictionary object that
         * is expected to be a name and convert it to a string.  Null is 
returned
-        * if the entry does not exist in the dictionary.
+        * if the entry does not exist in the dictionary or if the date was 
invalid.
         *
         * @param key The key to the item in the dictionary.
-        * @return The name converted to a string.
-        *
-        * @throws IOException If there is an error converting to a date.
+        * @return The name converted to a date.
         */
-       public Calendar getDate( COSName key ) throws IOException
+       public Calendar getDate( COSName key )
        {
                COSString date = (COSString)getDictionaryObject( key );
                return DateConverter.toCalendar( date );
@@ -800,14 +797,13 @@ public class COSDictionary extends COSBa
        /**
         * This is a convenience method that will get the dictionary object that
         * is expected to be a date.  Null is returned
-        * if the entry does not exist in the dictionary.
+        * if the entry does not exist in the dictionary or if the date was 
invalid.
         *
         * @param key The key to the item in the dictionary.
         * @param defaultValue The default value to return.
-        * @return The name converted to a string.
-        * @throws IOException If there is an error converting to a date.
+        * @return The name converted to a date.
         */
-       public Calendar getDate( String key, Calendar defaultValue ) throws 
IOException
+       public Calendar getDate( String key, Calendar defaultValue )
        {
                return getDate( COSName.getPDFName( key ), defaultValue );
        }
@@ -815,14 +811,13 @@ public class COSDictionary extends COSBa
        /**
         * This is a convenience method that will get the dictionary object that
         * is expected to be a date.  Null is returned
-        * if the entry does not exist in the dictionary.
+        * if the entry does not exist in the dictionary or if the date was 
invalid.
         *
         * @param key The key to the item in the dictionary.
         * @param defaultValue The default value to return.
-        * @return The name converted to a string.
-        * @throws IOException If there is an error converting to a date.
+        * @return The name converted to a date.
         */
-       public Calendar getDate( COSName key, Calendar defaultValue ) throws 
IOException
+       public Calendar getDate( COSName key, Calendar defaultValue )
        {
                Calendar retval = getDate( key );
                if( retval == null )

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
URL: 
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java?rev=1567840&r1=1567839&r2=1567840&view=diff
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
 (original)
+++ 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
 Thu Feb 13 03:35:20 2014
@@ -265,14 +265,7 @@ public class PDSignature implements COSO
    */
   public Calendar getSignDate()
   {
-    try
-    {
-      return dictionary.getDate(COSName.M);
-    }
-    catch (IOException e)
-    {
-      return null;
-    }
+    return dictionary.getDate(COSName.M);
   }
 
   /**

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/DateConverter.java
URL: 
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/DateConverter.java?rev=1567840&r1=1567839&r2=1567840&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/DateConverter.java 
(original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/DateConverter.java 
Thu Feb 13 03:35:20 2014
@@ -16,7 +16,6 @@
  */
 package org.apache.pdfbox.util;
 
-import java.io.IOException;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -28,59 +27,52 @@ import java.util.TimeZone;
 
 import org.apache.pdfbox.cos.COSString;
 
-/**
+/*
  * Date format is described in PDF Reference 1.7 section 3.8.2
  * (www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
- * and also in PDF 32000-1:2008 
+ * and also in PDF 32000-1:2008
  * (http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf))
  * although the latter inexplicably omits the trailing apostrophe.
- * 
- * The interpretation of dates without timezones is unclear. 
+ *
+ * The interpretation of dates without timezones is unclear.
  * The code below assumes that such dates are in UTC+00 (aka GMT).
  * This is in keeping with the PDF Reference's assertion that:
- *      numerical fields default to zero values. 
+ *      numerical fields default to zero values.
  * However, the Reference does go on to make the cryptic remark:
- *      If no UT information is specified, the relationship of the specified  
- *      time to UT is considered to be unknown. Whether or not the time 
+ *      If no UT information is specified, the relationship of the specified
+ *      time to UT is considered to be unknown. Whether or not the time
  *      zone is known, the rest of the date should be specified in local time.
- * I understand this to refer to _creating_ a pdf date value. That is, 
- * code that can get the wall clock time and cannot get the timezone 
+ * I understand this to refer to _creating_ a pdf date value. That is,
+ * code that can get the wall clock time and cannot get the timezone
  * should write the wall clock time with a time zone of zero.
  * When _parsing_ a PDF date, the statement talks about "the rest of the date"
  * being local time, thus explicitly excluding the use of the local time
  * for the time zone.
-*/ 
+*/
 
 /**
- * This class is used to convert dates to strings and back using the PDF
- * date standard in section 3.8.2 of PDF Reference 1.7.  
+ * Converts dates to strings and back using the PDF date standard
+ * in section 3.8.2 of PDF Reference 1.7.
  *
- * @author <a href="mailto:b...@benlitchfield.com";>Ben Litchfield</a>
- * @author <a href="mailto:zweibie...@ahoo.com";>Fred Hansen</a>
+ * @author Ben Litchfield
+ * @author Fred Hansen
  * 
  * TODO Move members of this class elsewhere for shared use in pdfbox, xmpbox, 
and jempbox.
  */
-public class DateConverter
+public final class DateConverter
 {
+    private DateConverter()
+    {
+    }
+
     // milliseconds/1000 = seconds; seconds / 60 = minutes; minutes/60 = hours
     private static final int MINUTES_PER_HOUR = 60;
     private static final int SECONDS_PER_MINUTE = 60;
     private static final int MILLIS_PER_MINUTE = SECONDS_PER_MINUTE*1000;
     private static final int MILLIS_PER_HOUR = MINUTES_PER_HOUR * 
MILLIS_PER_MINUTE;
-    private static final int 
-            HALF_DAY = 12 * MINUTES_PER_HOUR * MILLIS_PER_MINUTE, 
-            DAY = 2*HALF_DAY;
-    
-    /**
-     * Error value if date is invalid. Parsing is done with 
-     * GregorianCalendar.setLenient(false), so every date field value
-     * must be within bounds. If an attempt is made to parse an invalid date 
-     * field, toCalendar(String, String[]) returns Jan 1 in year INVALID_YEAR.
-     */
-    public static final int INVALID_YEAR = 999;
-    
-    
-    /**
+    private static final int HALF_DAY = 12 * MINUTES_PER_HOUR * 
MILLIS_PER_MINUTE, DAY = 2*HALF_DAY;
+
+    /*
      * The Date format is supposed to be the PDF_DATE_FORMAT, but other
      * forms appear. These lists offer alternatives to be tried 
      * if parseBigEndianDate fails.  
@@ -110,13 +102,13 @@ public class DateConverter
      */
     private static final String[] ALPHA_START_FORMATS = 
     {
-            "EEEE, dd MMM yy hh:mm:ss a",
-            "EEEE, MMM dd, yy hh:mm:ss a",
-            "EEEE, MMM dd, yy 'at' hh:mma", // Acrobat Net Distiller 1.0 for 
Windows
-            "EEEE, MMM dd, yy", // Acrobat Distiller 1.0.2 for Macintosh  && 
PDFBOX-465
-            "EEEE MMM dd, yy HH:mm:ss", // ECMP5
-            "EEEE MMM dd HH:mm:ss z yy", // GNU Ghostscript 7.0.7
-            "EEEE MMM dd HH:mm:ss yy", // GNU Ghostscript 7.0.7 variant
+        "EEEE, dd MMM yy hh:mm:ss a",
+        "EEEE, MMM dd, yy hh:mm:ss a",
+        "EEEE, MMM dd, yy 'at' hh:mma", // Acrobat Net Distiller 1.0 for 
Windows
+        "EEEE, MMM dd, yy", // Acrobat Distiller 1.0.2 for Macintosh  && 
PDFBOX-465
+        "EEEE MMM dd, yy HH:mm:ss", // ECMP5
+        "EEEE MMM dd HH:mm:ss z yy", // GNU Ghostscript 7.0.7
+        "EEEE MMM dd HH:mm:ss yy", // GNU Ghostscript 7.0.7 variant
     };
     
     private static final String[] DIGIT_START_FORMATS = 
@@ -155,28 +147,6 @@ public class DateConverter
             //      '' mapped to a single ', and the ''' was invalid)
     };
 
-
-    private DateConverter()
-    {
-        //utility class should not be constructed.
-    }
-
-    ////////////////////////////////////////////
-    // C o n v e r t   t o   S t r i n g   Methods
-     
-    /**
-     * Get all know formats.
-     * 
-     * @return an array containig all known formats
-     */
-    public static String[] getFormats() 
-    {
-        String[] val = new 
String[ALPHA_START_FORMATS.length+DIGIT_START_FORMATS.length];
-        System.arraycopy(ALPHA_START_FORMATS, 0, val, 0, 
ALPHA_START_FORMATS.length);
-        System.arraycopy(DIGIT_START_FORMATS, 0, 
val,ALPHA_START_FORMATS.length, DIGIT_START_FORMATS.length);
-        return val;
-    }
-
     /**
      * Converts a Calendar to a string formatted as:
      *     D:yyyyMMddHHmmss#hh'mm'  where # is Z, +, or -.
@@ -193,14 +163,14 @@ public class DateConverter
         {
             return null;
         }
-        String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET)
-                + cal.get(Calendar.DST_OFFSET), "'");
+        String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET) +
+                                       cal.get(Calendar.DST_OFFSET), "'");
         return String.format("D:"
                 + "%1$4tY%1$2tm%1$2td"   // yyyyMMdd 
                 + "%1$2tH%1$2tM%1$2tS"   // HHmmss 
-                + "%2$s"                // time zone
-                + "'",                  // trailing apostrophe
-            cal, offset);      
+                + "%2$s"                 // time zone
+                + "'",                   // trailing apostrophe
+            cal, offset);
     }
 
     /**
@@ -214,34 +184,32 @@ public class DateConverter
      */
     public static String toISO8601(Calendar cal)
     {
-        String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET)
-                + cal.get(Calendar.DST_OFFSET), ":");
+        String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET) +
+                                       cal.get(Calendar.DST_OFFSET), ":");
         return String.format(
-                "%1$4tY"   // yyyy
-                + "-%1$2tm"   // -mm  (%tm adds one to cal month value)
-                + "-%1$2td"  // -dd  (%tm adds one to cal month value)
-                + "T"                             // T
-                + "%1$2tH:%1$2tM:%1$2tS"   // HHmmss  
-                + "%2$s",              // time zone
-            cal, offset);      
+                "%1$4tY"                  // yyyy
+                + "-%1$2tm"               // -mm  (%tm adds one to cal month 
value)
+                + "-%1$2td"               // -dd  (%tm adds one to cal month 
value)
+                + "T"                     // T
+                + "%1$2tH:%1$2tM:%1$2tS"  // HHmmss
+                + "%2$s",                 // time zone
+            cal, offset);
     }
     
-    /**
+    /*
      * Constrain a timezone offset to the range  [-11:59 thru +11:59].
-     * @param proposedOffset A value intended to be a timezone offset.
-     * @return The corresponding value reduced to the above noted range 
      * by adding or subtracting multiples of a full day.
      */
-    public static int restrainTZoffset(long proposedOffset) 
+    private static int restrainTZoffset(long proposedOffset)
     {
-        proposedOffset = ((proposedOffset+HALF_DAY)%DAY+DAY)%DAY; 
+        proposedOffset = ((proposedOffset + HALF_DAY) % DAY + DAY) % DAY;
         // 0 <= proposedOffset < DAY
-        proposedOffset = (proposedOffset-HALF_DAY)%HALF_DAY;   
+        proposedOffset = (proposedOffset - HALF_DAY) % HALF_DAY;
         // -HALF_DAY < proposedOffset < HALF_DAY
         return (int)proposedOffset;
     }
     
-    /** 
+    /*
      * Formats a time zone offset as #hh^mm
      * where # is + or -, hh is hours, ^ is a separator, and mm is minutes.
      * Any separator may be specified by the second argument;
@@ -250,7 +218,7 @@ public class DateConverter
      * For offset of 0 millis, the String returned is "+00^00", never "Z".
      * To get a "general" offset in form GMT#hh:mm, write
      *      "GMT"+DateConverter.formatTZoffset(offset, ":");
-     * <p>
+     *
      * Take thought in choosing the source for the millis value. 
      * It can come from calendarValue.getTimeZone() or from 
      * calendarValue.get(Calendar.ZONE_OFFSET).  If a TimeZone was created
@@ -261,7 +229,7 @@ public class DateConverter
      * will not have a daylight savings rule. (Not even if there is a
      * known time zone with the given ID. To get the TimeZone named "xDT"
      * with its DST rule, use an ID of EST5EDT, CST6CDT, MST7MDT, or PST8PDT.
-     * <p>
+     *
      * When parsing PDF dates, the incoming values DOES NOT have a TIMEZONE 
value.
      * At most it has an OFFSET value like -04'00'. It is generally impossible 
to 
      * determine what TIMEZONE corresponds to a given OFFSET. If the date is
@@ -270,11 +238,11 @@ public class DateConverter
      * offset -0400 and no daylight saving. Or it might correspond to 
      * any one of the 31 regions (out of 43) that observe daylight savings 
      * and have standard time offset of -0500.
-     * <p>
+     *
      * If a Calendar has not been assigned a TimeZone with setTimeZone(), 
      * it will have by default the local TIMEZONE, not just the OFFSET.  In the
      * USA, this TimeZone will have a daylight savings rule.
-     * <p>
+     *
      * The offset assigned with calVal.set(Calendar.ZONE_OFFSET) differs
      * from the offset in the TimeZone set by Calendar.setTimeZone(). Example:
      * Suppose my local TimeZone is America/New_York. It has an offset of 
-05'00'.
@@ -286,7 +254,7 @@ public class DateConverter
      *     calVal.get(Calendar.ZONE_OFFSET) + calVal.get(Calendar.DST_OFFSET) 
=> -06:00
      *     calVal.getTimeZone().getRawOffset()  =>  -05:00
      *     calVal.getTimeZone().getOffset(calVal.getTimeInMillis())  =>  -04:00
-     * <p>
+     *
      * Which is correct??? I dunno, though setTimeZone() does seem to affect
      * ZONE_OFFSET, and not vice versa.  One cannot even test whether TimeZone 
      * or ZONE_OFFSET has been set; both have been set by initialization code.
@@ -295,63 +263,54 @@ public class DateConverter
      * 
      * My choice in this DateConverter class has been to set the 
      * initial TimeZone of a GregorianCalendar to GMT. Thereafter
-     * the TimeZone is modified with {@link #adjustTimeZoneNicely}. 
-     * 
-     * @param millis a time zone offset expressed in milliseconds
-     *      Any value is accepted; it is normalized to [-11:59 ... +11:59]
-     * @param sep a String to insert between hh and mm. May be empty.
-     * @return the formatted String for the offset
+     * the TimeZone is modified with {@link #adjustTimeZoneNicely}.
+     *
+     * package-private for testing
      */
-    public static String formatTZoffset(long millis, String sep) 
+    static String formatTZoffset(long millis, String sep)
     {
         SimpleDateFormat sdf = new SimpleDateFormat("Z"); // #hhmm
         sdf.setTimeZone(new 
SimpleTimeZone(restrainTZoffset(millis),"unknown"));
         String tz = sdf.format(new Date());
-        return tz.substring(0,3)+sep+tz.substring(3);
+        return tz.substring(0,3) + sep + tz.substring(3);
     }
 
-    //////////////////////////////////////////////
-    // P A R S E   Methods
-
-     /**
+     /*
      * Parses an integer from a string, starting at and advancing a 
ParsePosition.
-     * 
-     * @param text The string being parsed. If null, the remedy value is 
returned.
-     * @param where The ParsePosition to start the search. This value 
-     *      will be incremented by the number of digits found, but no 
-     *      more than maxlen.  That is, the ParsePosition will 
-     *      advance across at most maxlen initial digits in text.
-     *      The error index is ignored and unchanged.
-     * @param maxlen The maximum length of the integer to parse. 
-     *      Usually 2, but 4 for year fields.
-     *      If the field of length maxlen begins with a digit, 
-     *      but contains a non-digit, no error is signaled 
-     *      and the integer value is returned.
-     * @param remedy Value to be assigned if no digit is found at the
-     *      initial parse position; that is, if the field is empty.
-     * @return The integer that was at the given parse position. Or
-     *      the remedy value if no digits were found.
+     * Returns The integer that was at the given parse position, or the remedy 
value
+     * if no digits were found.
+     *
+     * The ParsePosition will be incremented by the number of digits found, 
but no
+     * more than maxlen. That is, the ParsePosition will advance across at most
+     * maxlen initial digits in text. The error index is ignored and unchanged.
+     *
+     * maxlen is the maximum length of the integer to parse, usually 2, but 4 
for
+     * year fields. If the field of length maxlen begins with a digit, but 
contains
+     * a non-digit, no error is signaled and the integer value is returned.
      */
-    public static int parseTimeField(String text, ParsePosition where, 
-            int maxlen, int remedy) 
+    private static int parseTimeField(String text, ParsePosition where, int 
maxlen, int remedy)
     {
         if (text == null) 
         {
-                    return remedy; 
+            return remedy;
         }
-        // (it would seem that DecimalFormat.parse() would be simpler;
-        //     but that class blithely ignores setMaximumIntegerDigits)
+        // it would seem that DecimalFormat.parse() would be simpler;
+        // but that class blithely ignores setMaximumIntegerDigits
         int retval = 0;
         int index = where.getIndex();
         int limit = index + Math.min(maxlen, text.length()-index);
         for (; index < limit; index++)
         {
-            int cval = text.charAt(index) - '0';  // convert digit to integer
-            if (cval <0 || cval > 9)   // test to see if we got a digit
+            // convert digit to integer
+            int cval = text.charAt(index) - '0';
+            // test to see if we got a digit
+            if (cval < 0 || cval > 9)
             {
-                break;   // no digit at index
+                // no digit at index
+                break;
             }
-            retval = retval*10 + cval;   // append the digit to the return 
value
+            // append the digit to the return value
+            retval = retval * 10 + cval;
         }   
         if (index == where.getIndex())
         {
@@ -361,28 +320,21 @@ public class DateConverter
         return retval;
     }
  
-    /**
-     * Advances the ParsePosition past any and all the characters 
-     *      that match those in the optionals list.
-     *      In particular, a space will skip all spaces.
-     * @param text The text to examine
-     * @param where index to start looking. 
-     *      The value is incremented by the number of optionals found.
-     *      The error index is ignored and unchanged.
-     * @param optionals A String listing all the optional characters 
-     *      to be skipped.
-     * @return The last non-space character passed over. 
-     *      Returns a space if no non-space character was found 
-     *      (even if space is not in the optionals list.)
+    /*
+     * Advances the ParsePosition past any and all the characters that match
+     * those in the optionals list. In particular, a space will skip all 
spaces.
+     *
+     * The start value is incremented by the number of optionals found. The 
error
+     * index is ignored and unchanged.
+     *
+     * Returns the last non-space character passed over (even if space is not 
in
+     * the optionals list.)
      */
-    public static char skipOptionals(String text, ParsePosition where, 
-            String optionals) 
+    private static char skipOptionals(String text, ParsePosition where, String 
optionals)
     {
         char retval = ' ', currch;
-        while (text != null && where.getIndex() < text.length() 
-                && optionals.indexOf(
-                        (currch=text.charAt(where.getIndex()))
-                ) >= 0) 
+        while (text != null && where.getIndex() < text.length() &&
+               optionals.indexOf((currch = text.charAt(where.getIndex()))) >= 
0)
         {
             retval = (currch != ' ') ? currch : retval;
             where.setIndex(where.getIndex() + 1);
@@ -390,18 +342,15 @@ public class DateConverter
         return retval;
     }
     
-    /**
-     * If the victim string is at the given position in the text,
-     * this method advances the position past that string. 
-     * 
-     * @param text The text to examine
-     * @param victim The string to look for
-     * @param where The initial position to look at. After return, this will
-     *      have been incremented by the length of the victim if it was found.
-     *      The error index is ignored and unchanged.
-     * @return true if victim was found; otherwise false.
+    /*
+     * If the victim string is at the given position in the text, this method
+     * advances the position past that string.
+     *
+     * `where` is the initial position to look at. After return, this will have
+     * been incremented by the length of the victim if it was found. The error
+     * index is ignored and unchanged.
      */
-    public static boolean skipString(String text, String victim, ParsePosition 
where) 
+    private static boolean skipString(String text, String victim, 
ParsePosition where)
     {
         if (text.startsWith(victim, where.getIndex()))
         {
@@ -411,15 +360,15 @@ public class DateConverter
         return false;
     }
 
-    /** 
+    /*
      * Construct a new GregorianCalendar and set defaults.
      * Locale is ENGLISH.
      * TimeZone is "UTC" (zero offset and no DST).
      * Parsing is NOT lenient. Milliseconds are zero.
-     * 
-     * @return a new gregorian calendar
+     *
+     * package-private for testing
      */
-    public static GregorianCalendar newGreg()  
+    static GregorianCalendar newGreg()
     {
         GregorianCalendar retCal = new GregorianCalendar(Locale.ENGLISH);
         retCal.setTimeZone(new SimpleTimeZone(0, "UTC"));
@@ -428,23 +377,21 @@ public class DateConverter
         return retCal;
     }
     
-    /**
+    /*
      * Install a TimeZone on a GregorianCalendar without changing the 
      * hours value. A plain GregorianCalendat.setTimeZone() 
      * adjusts the Calendar.HOUR value to compensate. This is *BAD*
      * (not to say *EVIL*) when we have already set the time.
-     * @param cal The GregorianCalendar whose TimeZone to change.
-     * @param tz The new TimeZone.
      */
-    public static void adjustTimeZoneNicely(GregorianCalendar cal, TimeZone 
tz) 
+    private static void adjustTimeZoneNicely(GregorianCalendar cal, TimeZone 
tz)
     {
         cal.setTimeZone(tz);
-        int offset = (cal.get(Calendar.ZONE_OFFSET) + 
cal.get(Calendar.DST_OFFSET))
-                / MILLIS_PER_HOUR;
+        int offset = (cal.get(Calendar.ZONE_OFFSET) + 
cal.get(Calendar.DST_OFFSET)) /
+                      MILLIS_PER_HOUR;
         cal.add(Calendar.HOUR, -offset);
     }
     
-    /**
+    /*
      * Parses the end of a date string for a time zone and, if one is found,
      * sets the time zone of the GregorianCalendar. Otherwise the calendar 
      * time zone is unchanged.
@@ -457,26 +404,22 @@ public class DateConverter
      * This scheme accepts the format of PDF, RFC 822, and ISO8601. 
      * If none of these applies (as for a time zone name), we try
      * TimeZone.getTimeZone().
-     * 
-     * @param text The text expected to begin with a time zone value,
-     * possibly with leading or trailing spaces.
-     * @param cal The Calendar whose TimeZone to set. 
-     * @param initialWhere where Scanning begins at where.index. After 
success, the returned
-     *      index is that of the next character after the recognized string.
-     *      The error index is ignored and unchanged.
-     * @return true if parsed a time zone value; otherwise the 
-     *      time zone is unchanged and the return value is false.
+     *
+     * Scanning begins at where.index. After success, the returned index
+     * is that of the next character after the recognized string.
+     *
+     * package-private for testing
      */
-    public static boolean parseTZoffset(String text, GregorianCalendar cal, 
-            ParsePosition initialWhere) 
+    static boolean parseTZoffset(String text, GregorianCalendar cal,
+                                        ParsePosition initialWhere)
     {
         ParsePosition where = new ParsePosition(initialWhere.getIndex());
         TimeZone tz = new SimpleTimeZone(0, "GMT");
         int tzHours, tzMin;
         char sign = skipOptionals(text, where, "Z+- ");
-        boolean hadGMT = (sign == 'Z' || skipString(text, "GMT", where) 
-                || skipString(text, "UTC", where));
-        sign = ( ! hadGMT) ? sign : skipOptionals(text, where, "+- "); 
+        boolean hadGMT = (sign == 'Z' || skipString(text, "GMT", where) ||
+                         skipString(text, "UTC", where));
+        sign = (!hadGMT) ? sign : skipOptionals(text, where, "+- ");
         
         tzHours = parseTimeField(text, where, 2, -999);
         skipOptionals(text, where, "\': ");
@@ -484,23 +427,27 @@ public class DateConverter
         skipOptionals(text, where, "\' "); 
         
         if (tzHours != -999) 
-        {                    // we parsed a time zone in default format
-            int hrSign = (sign == '-' ? -1 :+1);
-            tz.setRawOffset(restrainTZoffset(hrSign*(tzHours*MILLIS_PER_HOUR + 
tzMin*MILLIS_PER_MINUTE))); 
+        {
+            // we parsed a time zone in default format
+            int hrSign = (sign == '-' ? -1 : 1);
+            tz.setRawOffset(restrainTZoffset(hrSign * (tzHours * 
MILLIS_PER_HOUR + tzMin *
+                                                       MILLIS_PER_MINUTE)));
             tz.setID("unknown");
         }
         else if ( ! hadGMT)
-        {            // try to process as a name; "GMT" or "UTC" has already 
been processed
+        {
+            // try to process as a name; "GMT" or "UTC" has already been 
processed
             String tzText = text.substring(initialWhere.getIndex()).trim();
             tz = TimeZone.getTimeZone(tzText);
             // getTimeZone returns "GMT" for unknown ids
             if ("GMT".equals(tz.getID()))  
-            {                // no timezone in text
-                // cal amd initialWhere are unchanged
+            {
+                // no timezone in text, cal amd initialWhere are unchanged
                 return false;
             }
             else
-            {                // we got a tz by name; use it
+            {
+                // we got a tz by name; use it
                 where.setIndex(text.length());
             }
         }
@@ -509,9 +456,9 @@ public class DateConverter
         return true;
     }
     
-    /**
+    /*
      * Parses a big-endian date: year month day hour min sec.
-     * The year must be four digits. Other fields may be adjacent 
+     * The year must be four digits. Other fields may be adjacent
      * and delimited by length or they may follow appropriate delimiters.
      *     year [ -/]* month [ -/]* dayofmonth [ T]* hour [:] min [:] sec 
[.secFraction]
      * If any numeric field is omitted, all following fields must also be 
omitted.
@@ -520,16 +467,11 @@ public class DateConverter
      * Ambiguous dates can produce unexpected results. For example:
      *      1970 12 23:08 will parse as 1970 December 23 00:08:00 
      * 
-     * @param text The string to parse.
-     * 
-     * @param initialWhere Where to begin the parse. On return the index
-     *      is advanced to just beyond the last character processed.
-     *      The error index is ignored and unchanged.
-     * 
-     * @return a GregorianCalendar representing the parsed date. 
-     *      Or null if the text did not begin with at least four digits.
+     * The parse begins at `where, on return the index
+     * is advanced to just beyond the last character processed.
+     * The error index is ignored and unchanged.
      */
-    public static GregorianCalendar parseBigEndianDate(String text, 
+    private static GregorianCalendar parseBigEndianDate(String text,
             ParsePosition initialWhere) 
     {
         ParsePosition where = new ParsePosition(initialWhere.getIndex());
@@ -559,7 +501,8 @@ public class DateConverter
         try 
         {
             dest.set(year, month, day, hour, minute, second);
-            dest.getTimeInMillis();    // trigger limit tests
+            // trigger limit tests
+            dest.getTimeInMillis();
         }
         catch (IllegalArgumentException ill) 
         {
@@ -567,30 +510,25 @@ public class DateConverter
         }
         initialWhere.setIndex(where.getIndex());
         skipOptionals(text, initialWhere, " ");
-        return dest;    // dest has at least a year value
+        // dest has at least a year value
+        return dest;
     }
 
-    /**
-     * See if text can be parsed as a date according to any of a list of 
+    /*
+     * See if text can be parsed as a date according to any of a list of
      * formats. The time zone may be included as part of the format, or
      * omitted in favor of later testing for a trailing time zone.
      * 
-     * @param text The text to be parsed.
-     * 
-     * @param fmts A list of formats to be tried. The syntax is that for 
-     *      {@link java.text.SimpleDateFormat}
-     * 
-     * @param initialWhere At start this is the position to begin
-     *      examining the text. Upon return it will have been
-     *      incremented to refer to the next non-space character after the 
date.
-     *      If no date was found, the value is unchanged.
-     *      The error index is ignored and unchanged.
-     * 
-     * @return null for failure to find a date, or the GregorianCalendar
-     *      for the date that was found. Unless a time zone was 
-     *      part of the format, the time zone will be GMT+0
+     * The parse starts at `where`, upon return it will have been
+     * incremented to refer to the next non-space character after the date.
+     * If no date was found, the value is unchanged.
+     * The error index is ignored and unchanged.
+     * 
+     * If there is a failure to find a date, or the GregorianCalendar
+     * for the date that was found. Unless a time zone was
+     * part of the format, the time zone will be GMT+0
      */
-    public static GregorianCalendar parseSimpleDate(String text, String[] 
fmts, 
+    private static GregorianCalendar parseSimpleDate(String text, String[] 
fmts,
             ParsePosition initialWhere) 
     {
         for(String fmt : fmts)
@@ -609,8 +547,7 @@ public class DateConverter
         return null;
     }
 
-    
-    /**
+    /*
      * Parses a String to see if it begins with a date, and if so, 
      * returns that date. The date must be strictly correct--no 
      * field may exceed the appropriate limit.
@@ -627,27 +564,19 @@ public class DateConverter
      * - PDF format dates are among those recognized by parseBigEndianDate.
      * - The formats tried are alphaStartFormats or digitStartFormat and
      * any listed in the value of moreFmts.
-     * 
-     * @param text The String that may begin with a date. Must not be null.
-     *      Initial spaces and "D:" are skipped over.
-     * @param moreFmts Additional formats to be tried after trying the
-     *      built-in formats.
-     * @param initialWhere where Parsing begins at the given position in text. 
If the
-     *      parse succeeds, the index of where is advanced to point 
-     *      to the first unrecognized character.
-     *      The error index is ignored and unchanged.
-     * @return A GregorianCalendar for the date. If no date is found, 
-     *      returns null. The time zone will be GMT+0 unless parsing 
-     *      succeeded with a format containing a time zone. (Only one
-     *      builtin format contains a time zone.)
-     * 
      */
-    public static Calendar parseDate(String text, String[] moreFmts, 
-            ParsePosition initialWhere) 
+    private static Calendar parseDate(String text, ParsePosition initialWhere)
     {
-        // place to remember longestr date string
-        int longestLen = -999999;  // theorem: this value will never be used
-                // proof: longestLen is only used if longestDate is not null
+        if (text == null || text.equals(""))
+        {
+            return null;
+        }
+
+        // remember longestr date string
+        int longestLen = -999999;
+        // theorem: the above value will never be used
+        // proof: longestLen is only used if longestDate is not null
+
         GregorianCalendar longestDate = null; // null says no date found yet
         int whereLen;   // tempcopy of where.getIndex()
         
@@ -659,12 +588,10 @@ public class DateConverter
         // try big-endian parse
         GregorianCalendar retCal = parseBigEndianDate(text, where);
         // check for success and a timezone
-        if (retCal != null &&
-                (where.getIndex() == text.length() 
-                || parseTZoffset(text, retCal, where))) 
+        if (retCal != null && (where.getIndex() == text.length() ||
+                               parseTZoffset(text, retCal, where)))
         {
-            // if text is fully consumed, return the date
-            // else remember it and its length
+            // if text is fully consumed, return the date else remember it and 
its length
             whereLen = where.getIndex();
             if (whereLen == text.length()) 
             {
@@ -683,12 +610,11 @@ public class DateConverter
                 : ALPHA_START_FORMATS;
         retCal = parseSimpleDate(text, formats, where);
         // check for success and a timezone
-        if (retCal != null && 
-                (where.getIndex() == text.length() 
-                || parseTZoffset(text, retCal, where)))
+        if (retCal != null &&
+                (where.getIndex() == text.length() ||
+                 parseTZoffset(text, retCal, where)))
         {
-            // if text is fully consumed, return the date
-            // else remember it and its length
+            // if text is fully consumed, return the date else remember it and 
its length
             whereLen = where.getIndex();
             if (whereLen == text.length()) 
             {
@@ -701,27 +627,7 @@ public class DateConverter
                 longestDate = retCal;
             }
         }
-        
-        // try the supplied formats
-        if (moreFmts != null)
-        {
-            where.setIndex(startPosition);
-            retCal = parseSimpleDate(text, moreFmts, where);
-            if (retCal != null && 
-                (where.getIndex() == text.length() 
-                || parseTZoffset(text, retCal, where)))
-            {
-                whereLen = where.getIndex();
-                // if text is fully consumed, return the date
-                // else remember it and its length
-                if (whereLen == text.length() || 
-                        (longestDate != null && whereLen > longestLen)) 
-                {
-                    initialWhere.setIndex(whereLen);
-                    return retCal;
-                }
-            }
-        }
+
         if (longestDate != null) 
         {
             initialWhere.setIndex(longestLen);
@@ -731,21 +637,15 @@ public class DateConverter
     }
        
     /**
-     * Converts a string to a Calendar by parsing the String for a date.
-     * @see #toCalendar(String).
+     * Returns the Calendar for a given COS string containing a date,
+     * or {@code null} if it cannot be parsed.
      *
      * The returned value will have 0 for DST_OFFSET.
      * 
-     * @param text The COSString representation of a date.
-     * @return The Calendar that the text string represents. 
-     *      Or null if text was null.
-     * @throws IOException If the date string is not in the correct format.
-     * @deprecated This method throws an IOException for failure. Replace
-     *      calls to it with {@link #toCalendar(String, String[])} 
-     *      and test for failure with
-     *          (value == null || value.get(Calendar.YEAR) == INVALID_YEAR)
+     * @param text A COS string containing a date.
+     * @return The Calendar that the text string represents, or {@code null} 
if it cannot be parsed.
      */
-    public static Calendar toCalendar(COSString text) throws IOException
+    public static Calendar toCalendar(COSString text)
     {
         if (text == null)
         {
@@ -753,66 +653,33 @@ public class DateConverter
         }
         return toCalendar(text.getString());
     }
-    
+
     /**
-     * Converts a string date to a Calendar date value; equivalent to 
-     * {@link #toCalendar(String, String[])} using <pre>null</pre> for the 
second parameter, 
-     * but throws an IOException for failure.
-     * 
+     * Returns the Calendar for a given string containing a date,
+     * or {@code null} if it cannot be parsed.
+     *
      * The returned value will have 0 for DST_OFFSET.
-     * 
-     * @param text The string representation of the calendar.
-     * @return The Calendar that this string represents 
-     *      or null if the incoming text is null.
-     * @throws IOException If the date string is non-null 
-     *      and not a parseable date.
-     * @deprecated This method throws an IOException for failure. Replace
-     *      calls to it with {@link #toCalendar(String, String[])} 
-     *      using <pre>null</pre> for the second parameter
-     *      and test for failure with
-     *          (value == null || value.get(Calendar.YEAR) == INVALID_YEAR)
+     *
+     * @param text A COS string containing a date.
+     * @return The Calendar that the text string represents, or {@code null} 
if it cannot be parsed.
      */
-    public static Calendar toCalendar(String text) throws IOException
+    public static Calendar toCalendar(String text)
     {
-        if (text == null)
-        {
-            return null;    
-        }
-        Calendar val = toCalendar(text, null);
-        if (val != null && val.get(Calendar.YEAR) == INVALID_YEAR)  
+        if (text == null || text.trim().equals(""))
         {
-            throw new IOException("Error converting date: " + text);
+            return null;
         }
-        return val;
-    }
-    
-    /**
-     * Converts a string to a calendar. The entire string must be consumed.
-     * The date must be strictly correct; that is, no field may exceed
-     * the appropriate limit. Uses {@link #parseDate} to do the actual parsing.
-     * 
-     * The returned value will have 0 for DST_OFFSET.
-     * 
-     * @param text The text to parse. Initial spaces and "D:" are skipped over.
-     * @param moreFmts An Array of formats (as Strings) to try 
-     *      in addition to the standard list.
-     * @return the Calendar value corresponding to the date text. 
-     *      If text does not represent a valid date, 
-     *      the value is January 1 on year INVALID_YEAR at 0:0:0 GMT.
-     * 
-     */
-    public static Calendar toCalendar(String text, String[] moreFmts)
-    {
+
         ParsePosition where = new ParsePosition(0);
         skipOptionals(text, where, " ");
         skipString(text, "D:", where);
-        Calendar retCal = parseDate(text, moreFmts, where);   // PARSE THE TEXT
-        if (retCal == null || where.getIndex() != text.length()) 
+        Calendar calendar = parseDate(text, where);
+
+        if (calendar == null || where.getIndex() != text.length())
         {
-            // the date string is invalid for all formats we tried,
-            retCal = newGreg();
-            retCal.set(INVALID_YEAR, 0, 1, 0, 0, 0);
+            // the date string is invalid
+            return null;
         }
-        return retCal;
+        return calendar;
     }
 }

Modified: 
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java
URL: 
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java?rev=1567840&r1=1567839&r2=1567840&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java 
(original)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java 
Thu Feb 13 03:35:20 2014
@@ -69,31 +69,15 @@ public class TestDateUtil extends TestCa
     {
         TimeZone timezone = TimeZone.getDefault();
         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
-        try 
-        {
-            assertCalendarEquals( new GregorianCalendar( 2005, 4, 12 ), 
-                    DateConverter.toCalendar( "D:05/12/2005" ) );
-            assertCalendarEquals( new GregorianCalendar( 2005, 4,12,15,57,16 
), 
-                    DateConverter.toCalendar( "5/12/2005 15:57:16" ) );
-        }
-        catch (IOException ex) 
-        {
-            ex.printStackTrace();
-        }
-        finally 
-        {
-            TimeZone.setDefault(timezone);
-        }
-        // check that new toCalendar gives NullPointer for a null arg
-        try 
-        { 
-            DateConverter.toCalendar(null, null);
-            assertNotNull(null);    // failed to have expected exception
-        } 
-        catch (NullPointerException ex) 
-        {
-            // expected outcome
-        }   
+
+        assertCalendarEquals( new GregorianCalendar( 2005, 4, 12 ),
+                DateConverter.toCalendar( "D:05/12/2005" ) );
+        assertCalendarEquals( new GregorianCalendar( 2005, 4,12,15,57,16 ),
+                DateConverter.toCalendar( "5/12/2005 15:57:16" ) );
+
+        TimeZone.setDefault(timezone);
+        // check that new toCalendarSTATIC gives null for a null arg
+        assertNull(DateConverter.toCalendar((String)null));
     }
     
     /**
@@ -128,7 +112,7 @@ public class TestDateUtil extends TestCa
     }
 
     /**
-     * Check toCalendar.
+     * Check toCalendarSTATIC.
      * @param yr expected year value
      *  If an IOException is the expected result, yr should be null
      * @param mon expected month value
@@ -136,7 +120,7 @@ public class TestDateUtil extends TestCa
      * @param hr expected hour value
      * @param min expected minute value
      * @param sec expected second value
-     * @param tz represents expected timezone offset 
+     * @param offset represents expected timezone offset
      * @param orig  A date to be parsed.
      * @throws Exception If an unexpected error occurs.
      */
@@ -149,25 +133,17 @@ public class TestDateUtil extends TestCa
         String iso8601Date = String.format("%04d-%02d-%02d"
                 + "T%02d:%02d:%02d%+03d:00", 
                 yr,mon,day,hr,min,sec,offset);
-        Calendar cal = null;
-        try 
-        {
-            cal = DateConverter.toCalendar(orig);
-        }
-        catch (IOException ex) 
-        {
-            assertEquals(yr, BAD);
-        }
+        Calendar cal = DateConverter.toCalendar(orig);
         if (cal != null) 
         {
             assertEquals(iso8601Date, DateConverter.toISO8601(cal));
             assertEquals(pdfDate, DateConverter.toString(cal));
         }
-        // new toCalendar()
-        cal = DateConverter.toCalendar(orig, null);
+        // new toCalendarSTATIC()
+        cal = DateConverter.toCalendar(orig);
         if (yr == BAD) 
         {
-            assertEquals(cal.get(Calendar.YEAR), DateConverter.INVALID_YEAR);
+            assertEquals(null, cal);
         }
         else
         {
@@ -368,8 +344,8 @@ public class TestDateUtil extends TestCa
         checkToString(1991, 11, 1, 1, 14, 15, tzMcMurdo, +0);
         checkToString(1992, 12, 1, 1, 14, 15, tzMcMurdo, +0);
     }
-   
-    private static void checkParseTZ(int expect, String src) 
+
+    private static void checkParseTZ(int expect, String src)
     {
         GregorianCalendar dest = DateConverter.newGreg();
         DateConverter.parseTZoffset(src, dest, new ParsePosition(0));


Reply via email to