8033980: Xerces Update: datatype XMLGregorianCalendarImpl and DurationImpl
authorjoehw
Tue, 18 Feb 2014 10:51:16 -0800
changeset 23088 811074ec5431
parent 22674 bbf861d09bde
child 23089 ef8484eeca3a
8033980: Xerces Update: datatype XMLGregorianCalendarImpl and DurationImpl Reviewed-by: dfuchs, lancea, alanb
jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java
jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/XMLGregorianCalendarImpl.java
--- a/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java	Tue Jan 28 11:22:03 2014 -0800
+++ b/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java	Tue Feb 18 10:51:16 2014 -0800
@@ -3,11 +3,12 @@
  * DO NOT REMOVE OR ALTER!
  */
 /*
- * Copyright 2005 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
@@ -106,24 +107,20 @@
         extends Duration
         implements Serializable {
 
-    /**
-     * <p>Number of Fields.</p>
-     */
-    private static final int FIELD_NUM = 6;
 
     /**
      * <p>Internal array of value Fields.</p>
      */
-        private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
-                        DatatypeConstants.YEARS,
-                        DatatypeConstants.MONTHS,
-                        DatatypeConstants.DAYS,
-                        DatatypeConstants.HOURS,
-                        DatatypeConstants.MINUTES,
-                        DatatypeConstants.SECONDS
-                };
+    private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
+        DatatypeConstants.YEARS,
+        DatatypeConstants.MONTHS,
+        DatatypeConstants.DAYS,
+        DatatypeConstants.HOURS,
+        DatatypeConstants.MINUTES,
+        DatatypeConstants.SECONDS
+    };
 
-                /**
+    /**
                  * <p>Internal array of value Field ids.</p>
                  */
                 private static final int[] FIELD_IDS = {
@@ -141,9 +138,9 @@
     private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
 
         /**
-         * <p>BigDecimal value of 0.</p>
-         */
-        private static final BigDecimal ZERO = BigDecimal.valueOf((long) 0);
+     * <p>BigDecimal value of 0.</p>
+     */
+    private static final BigDecimal ZERO = BigDecimal.valueOf(0);
 
     /**
      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
@@ -186,17 +183,17 @@
      */
     protected BigDecimal seconds;
 
-        /**
-         * Returns the sign of this duration in -1,0, or 1.
-         *
-         * @return
-         *      -1 if this duration is negative, 0 if the duration is zero,
-         *      and 1 if the duration is postive.
-         */
-        public int getSign() {
+    /**
+     * Returns the sign of this duration in -1,0, or 1.
+     *
+     * @return
+     *      -1 if this duration is negative, 0 if the duration is zero,
+     *      and 1 if the duration is postive.
+     */
+    public int getSign() {
 
-                return signum;
-        }
+        return signum;
+    }
 
         /**
          * TODO: Javadoc
@@ -206,20 +203,20 @@
          */
     protected int calcSignum(boolean isPositive) {
         if ((years == null || years.signum() == 0)
-            && (months == null || months.signum() == 0)
-            && (days == null || days.signum() == 0)
-            && (hours == null || hours.signum() == 0)
-            && (minutes == null || minutes.signum() == 0)
-            && (seconds == null || seconds.signum() == 0)) {
+             && (months == null || months.signum() == 0)
+             && (days == null || days.signum() == 0)
+             && (hours == null || hours.signum() == 0)
+             && (minutes == null || minutes.signum() == 0)
+             && (seconds == null || seconds.signum() == 0)) {
             return 0;
-            }
+        }
 
-            if (isPositive) {
-                return 1;
-            } else {
-                return -1;
-            }
-
+        if (isPositive) {
+            return 1;
+        }
+        else {
+            return -1;
+        }
     }
 
     /**
@@ -357,7 +354,7 @@
         }
 
         // int -> BigInteger
-        return new BigInteger(String.valueOf(i));
+        return BigInteger.valueOf(i);
     }
 
     /**
@@ -373,14 +370,16 @@
 
         if (l > 0) {
             signum = 1;
-        } else if (l < 0) {
+        }
+        else if (l < 0) {
             signum = -1;
             if (l == 0x8000000000000000L) {
                 // negating 0x8000000000000000L causes an overflow
                 l++;
             }
             l *= -1;
-        } else {
+        }
+        else {
             signum = 0;
         }
 
@@ -454,21 +453,22 @@
         throws IllegalArgumentException {
         // only if I could use the JDK1.4 regular expression ....
 
+        if (lexicalRepresentation == null) {
+           throw new NullPointerException();
+        }
+
         final String s = lexicalRepresentation;
         boolean positive;
         int[] idx = new int[1];
         int length = s.length();
         boolean timeRequired = false;
 
-        if (lexicalRepresentation == null) {
-            throw new NullPointerException();
-        }
-
         idx[0] = 0;
         if (length != idx[0] && s.charAt(idx[0]) == '-') {
             idx[0]++;
             positive = false;
-        } else {
+        }
+        else {
             positive = true;
         }
 
@@ -484,8 +484,8 @@
         String[] dateParts = new String[3];
         int[] datePartsIndex = new int[3];
         while (length != idx[0]
-            && isDigit(s.charAt(idx[0]))
-            && dateLen < 3) {
+               && isDigit(s.charAt(idx[0]))
+               && dateLen < 3) {
             datePartsIndex[dateLen] = idx[0];
             dateParts[dateLen++] = parsePiece(s, idx);
         }
@@ -493,7 +493,8 @@
         if (length != idx[0]) {
             if (s.charAt(idx[0]++) == 'T') {
                 timeRequired = true;
-            } else {
+            }
+            else {
                 throw new IllegalArgumentException(s); // ,idx[0]-1);
             }
         }
@@ -502,8 +503,8 @@
         String[] timeParts = new String[3];
         int[] timePartsIndex = new int[3];
         while (length != idx[0]
-            && isDigitOrPeriod(s.charAt(idx[0]))
-            && timeLen < 3) {
+                             && isDigitOrPeriod(s.charAt(idx[0]))
+                             && timeLen < 3) {
             timePartsIndex[timeLen] = idx[0];
             timeParts[timeLen++] = parsePiece(s, idx);
         }
@@ -604,6 +605,9 @@
 
         int idx = tokens.length();
         for (int i = len - 1; i >= 0; i--) {
+            if (parts[i] == null) {
+                throw new IllegalArgumentException(whole);
+            }
             int nidx =
                 tokens.lastIndexOf(
                     parts[i].charAt(parts[i].length() - 1),
@@ -722,8 +726,7 @@
          */
     public int compare(Duration rhs) {
 
-        BigInteger maxintAsBigInteger = BigInteger.valueOf((long) Integer.MAX_VALUE);
-        BigInteger minintAsBigInteger = BigInteger.valueOf((long) Integer.MIN_VALUE);
+        BigInteger maxintAsBigInteger = BigInteger.valueOf(Integer.MAX_VALUE);
 
         // check for fields that are too large in this Duration
         if (years != null && years.compareTo(maxintAsBigInteger) == 1) {
@@ -778,7 +781,7 @@
         if (seconds != null && seconds.toBigInteger().compareTo(maxintAsBigInteger) == 1) {
                 throw new UnsupportedOperationException(
                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
-                            new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), seconds.toString()})
+                            new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), toString(seconds)})
 
                                         //this.getClass().getName() + "#compare(Duration duration)"
                                                 //+ " seconds too large to be supported by this implementation "
@@ -957,9 +960,9 @@
         return resultA;
     }
 
-    private int compareResults(int resultA, int resultB){
+    private int compareResults(int resultA, int resultB) {
 
-      if ( resultB == DatatypeConstants.INDETERMINATE ) {
+        if ( resultB == DatatypeConstants.INDETERMINATE ) {
             return DatatypeConstants.INDETERMINATE;
         }
         else if ( resultA!=resultB) {
@@ -1007,25 +1010,25 @@
         buf.append('P');
 
         if (years != null) {
-            buf.append(years + "Y");
+            buf.append(years).append('Y');
         }
         if (months != null) {
-            buf.append(months + "M");
+            buf.append(months).append('M');
         }
         if (days != null) {
-            buf.append(days + "D");
+            buf.append(days).append('D');
         }
 
         if (hours != null || minutes != null || seconds != null) {
             buf.append('T');
             if (hours != null) {
-                buf.append(hours + "H");
+                buf.append(hours).append('H');
             }
             if (minutes != null) {
-                buf.append(minutes + "M");
+                buf.append(minutes).append('M');
             }
             if (seconds != null) {
-                buf.append(toString(seconds) + "S");
+                buf.append(toString(seconds)).append('S');
             }
         }
 
@@ -1055,10 +1058,12 @@
         int insertionPoint = intString.length() - scale;
         if (insertionPoint == 0) { /* Point goes right before intVal */
             return "0." + intString;
-        } else if (insertionPoint > 0) { /* Point goes inside intVal */
+        }
+        else if (insertionPoint > 0) { /* Point goes inside intVal */
             buf = new StringBuffer(intString);
             buf.insert(insertionPoint, '.');
-        } else { /* We must insert zeros between point and intVal */
+        }
+        else { /* We must insert zeros between point and intVal */
             buf = new StringBuffer(3 - insertionPoint + intString.length());
             buf.append("0.");
             for (int i = 0; i < -insertionPoint; i++) {
@@ -1302,7 +1307,8 @@
         Number n = getField(field);
         if (n == null) {
             return 0;
-        } else {
+        }
+        else {
             return n.intValue();
         }
     }
@@ -1340,8 +1346,7 @@
     public long getTimeInMillis(final Calendar startInstant) {
         Calendar cal = (Calendar) startInstant.clone();
         addTo(cal);
-        return getCalendarTimeInMillis(cal)
-                    - getCalendarTimeInMillis(startInstant);
+        return getCalendarTimeInMillis(cal) - getCalendarTimeInMillis(startInstant);
     }
 
     /**
@@ -1451,13 +1456,13 @@
         int days = (int) (diff / (1000L * 60L * 60L * 24L));
 
         return new DurationImpl(
-            days >= 0,
-            null,
-            null,
-            wrap(Math.abs(days)),
-            (BigInteger) getField(DatatypeConstants.HOURS),
-            (BigInteger) getField(DatatypeConstants.MINUTES),
-            (BigDecimal) getField(DatatypeConstants.SECONDS));
+                days >= 0,
+                null,
+                null,
+                wrap(Math.abs(days)),
+                (BigInteger) getField(DatatypeConstants.HOURS),
+                (BigInteger) getField(DatatypeConstants.MINUTES),
+                (BigDecimal) getField(DatatypeConstants.SECONDS));
     }
 
     /**
@@ -1547,14 +1552,16 @@
                 } else {
                     carry = ZERO;
                 }
-            } else {
+            }
+            else {
                 carry = bd.multiply(FACTORS[i]);
             }
         }
 
         if (seconds != null) {
             buf[5] = seconds.multiply(factor).add(carry);
-        } else {
+        }
+        else {
             buf[5] = carry;
         }
 
@@ -1581,14 +1588,17 @@
         if (f == DatatypeConstants.SECONDS) {
             if (seconds != null) {
                 return seconds;
-            } else {
+            }
+            else {
                 return ZERO;
             }
-        } else {
+        }
+        else {
             BigInteger bi = (BigInteger) getField(f);
             if (bi == null) {
                 return ZERO;
-            } else {
+            }
+            else {
                 return new BigDecimal(bi);
             }
         }
@@ -1607,7 +1617,8 @@
         boolean canBeNull) {
         if (canBeNull && value.signum() == 0) {
             return null;
-        } else {
+        }
+        else {
             return value.unscaledValue();
         }
     }
@@ -1616,7 +1627,7 @@
      * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
      * FIELDS[i+1].
      */
-    private static final BigDecimal[] FACTORS = new BigDecimal[]{
+    private static final BigDecimal[] FACTORS = new BigDecimal[] {
         BigDecimal.valueOf(12),
         null/*undefined*/,
         BigDecimal.valueOf(24),
@@ -1964,10 +1975,20 @@
     }
 
     /**
+     * Returns time value in milliseconds
+     * @param cal A calendar object
+     * @return time value
+     *
+     * Diff from Xerces; Use JDK 1.5 feature.
+     */
+    private static long getCalendarTimeInMillis(Calendar cal) {
+        return cal.getTimeInMillis();
+    }
+
+    /**
      * <p>Stream Unique Identifier.</p>
      *
-     * <p>TODO: Serialization should use the XML string representation as
-     * the serialization format to ensure future compatibility.</p>
+     * <p>Serialization uses the lexical form returned by toString().</p>
      */
     private static final long serialVersionUID = 1L;
 
@@ -1996,25 +2017,10 @@
         }
 
         private Object readResolve() throws ObjectStreamException {
-            //            try {
             return new DurationImpl(lexical);
-            //            } catch( ParseException e ) {
-            //                throw new StreamCorruptedException("unable to parse "+lexical+" as duration");
-            //            }
         }
 
         private static final long serialVersionUID = 1L;
     }
 
-    /**
-     * Calls the {@link Calendar#getTimeInMillis} method.
-     * Prior to JDK1.4, this method was protected and therefore
-     * cannot be invoked directly.
-     *
-     * In future, this should be replaced by
-     * <code>cal.getTimeInMillis()</code>
-     */
-    private static long getCalendarTimeInMillis(Calendar cal) {
-        return cal.getTime().getTime();
-    }
 }
--- a/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/XMLGregorianCalendarImpl.java	Tue Jan 28 11:22:03 2014 -0800
+++ b/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/XMLGregorianCalendarImpl.java	Tue Feb 18 10:51:16 2014 -0800
@@ -25,6 +25,8 @@
 
 package com.sun.org.apache.xerces.internal.jaxp.datatype;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -195,6 +197,17 @@
         extends XMLGregorianCalendar
         implements Serializable, Cloneable {
 
+    /** Backup values **/
+    transient private BigInteger orig_eon;
+    transient private int orig_year = DatatypeConstants.FIELD_UNDEFINED;
+    transient private int orig_month = DatatypeConstants.FIELD_UNDEFINED;
+    transient private int orig_day = DatatypeConstants.FIELD_UNDEFINED;
+    transient private int orig_hour = DatatypeConstants.FIELD_UNDEFINED;
+    transient private int orig_minute = DatatypeConstants.FIELD_UNDEFINED;
+    transient private int orig_second = DatatypeConstants.FIELD_UNDEFINED;
+    transient private BigDecimal orig_fracSeconds;
+    transient private int orig_timezone = DatatypeConstants.FIELD_UNDEFINED;
+
     /**
      * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
      */
@@ -241,9 +254,14 @@
     private BigDecimal fractionalSecond = null;
 
     /**
-     * <p>Constant to represent a billion.</p>
+     * <p>BigInteger constant; representing a billion.</p>
      */
-    private static final BigInteger BILLION = new BigInteger("1000000000");
+    private static final BigInteger BILLION_B = new BigInteger("1000000000");
+
+    /**
+     * <p>int constant; representing a billion.</p>
+     */
+    private static final int BILLION_I = 1000000000;
 
     /**
      *   <p>Obtain a pure Gregorian Calendar by calling
@@ -441,6 +459,23 @@
                     //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
             );
         }
+
+        save();
+    }
+
+    /**
+     * save original values
+     */
+    private void save() {
+        orig_eon = eon;
+        orig_year = year;
+        orig_month = month;
+        orig_day = day;
+        orig_hour = hour;
+        orig_minute = minute;
+        orig_second = second;
+        orig_fracSeconds = fractionalSecond;
+        orig_timezone = timezone;
     }
 
     /**
@@ -479,14 +514,14 @@
         BigDecimal fractionalSecond,
         int timezone) {
 
-                setYear(year);
+        setYear(year);
         setMonth(month);
         setDay(day);
         setTime(hour, minute, second, fractionalSecond);
-                setTimezone(timezone);
+        setTimezone(timezone);
 
-                // check for validity
-                if (!isValid()) {
+        // check for validity
+        if (!isValid()) {
 
             throw new IllegalArgumentException(
                 DatatypeMessageFormatter.formatMessage(null,
@@ -519,8 +554,9 @@
                 );
                 */
 
-                }
+        }
 
+        save();
     }
 
     /**
@@ -547,17 +583,21 @@
         int hour,
         int minute,
         int second,
-                int millisecond,
+        int millisecond,
         int timezone) {
 
-                setYear(year);
+        setYear(year);
         setMonth(month);
         setDay(day);
         setTime(hour, minute, second);
-                setTimezone(timezone);
-                setMillisecond(millisecond);
+        setTimezone(timezone);
+        BigDecimal realMilliseconds = null;
+        if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
+            realMilliseconds = BigDecimal.valueOf(millisecond, 3);
+        }
+        setFractionalSecond(realMilliseconds);
 
-                if (!isValid()) {
+        if (!isValid()) {
 
             throw new IllegalArgumentException(
                 DatatypeMessageFormatter.formatMessage(null,
@@ -580,7 +620,9 @@
                     );
                  */
 
-                }
+        }
+
+        save();
     }
 
         /**
@@ -661,6 +703,7 @@
         // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
         int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
         this.setTimezone(offsetInMinutes);
+        save();
     }
 
     // Factories
@@ -1164,7 +1207,7 @@
             this.eon = null;
             this.year = DatatypeConstants.FIELD_UNDEFINED;
         } else {
-            BigInteger temp = year.remainder(BILLION);
+            BigInteger temp = year.remainder(BILLION_B);
             this.year = temp.intValue();
             setEon(year.subtract(temp));
         }
@@ -1187,12 +1230,13 @@
         if (year == DatatypeConstants.FIELD_UNDEFINED) {
             this.year = DatatypeConstants.FIELD_UNDEFINED;
             this.eon = null;
-        } else if (Math.abs(year) < BILLION.intValue()) {
+        }
+        else if (Math.abs(year) < BILLION_I) {
             this.year = year;
             this.eon = null;
         } else {
             BigInteger theYear = BigInteger.valueOf((long) year);
-            BigInteger remainder = theYear.remainder(BILLION);
+            BigInteger remainder = theYear.remainder(BILLION_B);
             this.year = remainder.intValue();
             setEon(theYear.subtract(remainder));
         }
@@ -1688,6 +1732,9 @@
         if (obj == null || !(obj instanceof XMLGregorianCalendar)) {
             return false;
         }
+        if (obj == this) {
+            return true;
+        }
         return compare((XMLGregorianCalendar) obj) == DatatypeConstants.EQUAL;
     }
 
@@ -1950,51 +1997,36 @@
         // no need to check for anything except for constraints
         // between fields.
 
-        //check if days in month is valid. Can be dependent on leap year.
-        if (getMonth() == DatatypeConstants.FEBRUARY) {
-            // years could not be set
-            int maxDays = 29;
-
-            if (eon == null) {
-                if(year!=DatatypeConstants.FIELD_UNDEFINED)
-                    maxDays = maximumDayInMonthFor(year,getMonth());
-            } else {
-                BigInteger years = getEonAndYear();
-                if (years != null) {
-                    maxDays = maximumDayInMonthFor(getEonAndYear(), DatatypeConstants.FEBRUARY);
+        // check if days in month is valid. Can be dependent on leap year.
+        if (month != DatatypeConstants.FIELD_UNDEFINED && day != DatatypeConstants.FIELD_UNDEFINED) {
+            if (year != DatatypeConstants.FIELD_UNDEFINED) {
+                if (eon == null) {
+                    if (day > maximumDayInMonthFor(year, month)) {
+                        return false;
+                    }
+                }
+                else if (day > maximumDayInMonthFor(getEonAndYear(), month)) {
+                    return false;
                 }
             }
-            if (getDay() > maxDays) {
+            // Use 2000 as a default since it's a leap year.
+            else if (day > maximumDayInMonthFor(2000, month)) {
                 return false;
             }
         }
 
         // http://www.w3.org/2001/05/xmlschema-errata#e2-45
-        if (getHour() == 24) {
-            if(getMinute() != 0) {
-                return false;
-            } else if (getSecond() != 0) {
-                return false;
-            }
+        if (hour == 24 && (minute != 0 || second != 0 ||
+                (fractionalSecond != null && fractionalSecond.compareTo(DECIMAL_ZERO) != 0))) {
+            return false;
         }
 
         // XML Schema 1.0 specification defines year value of zero as
         // invalid. Allow this class to set year field to zero
         // since XML Schema 1.0 errata states that lexical zero will
         // be allowed in next version and treated as 1 B.C.E.
-        if (eon == null) {
-            // optimize check.
-            if (year == 0) {
-                return false;
-            }
-        } else {
-            BigInteger yearField = getEonAndYear();
-            if (yearField != null) {
-                int result = compareField(yearField, BigInteger.ZERO);
-                if (result == DatatypeConstants.EQUAL) {
-                    return false;
-                }
-            }
+        if (eon == null && year == 0) {
+            return false;
         }
         return true;
     }
@@ -2213,7 +2245,7 @@
             int quotient;
             if (endMonth < 0) {
                 endMonth = (13 - 1) + endMonth + 1;
-                quotient = new BigDecimal(intTemp - 1).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_UP).intValue();
+                quotient = BigDecimal.valueOf(intTemp - 1).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_UP).intValue();
             } else {
                 quotient = (intTemp - 1) / (13 - 1);
                 endMonth += 1;
@@ -2259,18 +2291,20 @@
     private static final BigInteger SIXTY = BigInteger.valueOf(60);
     private static final BigInteger TWENTY_FOUR = BigInteger.valueOf(24);
     private static final BigInteger TWELVE = BigInteger.valueOf(12);
-    private static final BigDecimal DECIMAL_ZERO = new BigDecimal("0");
-    private static final BigDecimal DECIMAL_ONE = new BigDecimal("1");
-    private static final BigDecimal DECIMAL_SIXTY = new BigDecimal("60");
+    private static final BigDecimal DECIMAL_ZERO = BigDecimal.valueOf(0);
+    private static final BigDecimal DECIMAL_ONE = BigDecimal.valueOf(1);
+    private static final BigDecimal DECIMAL_SIXTY = BigDecimal.valueOf(60);
 
 
-    private static int daysInMonth[] = { 0,  // XML Schema months start at 1.
-                                       31, 28, 31, 30, 31, 30,
-                                       31, 31, 30, 31, 30, 31};
+    private static class DaysInMonth {
+        private static final int [] table = { 0,  // XML Schema months start at 1.
+            31, 28, 31, 30, 31, 30,
+            31, 31, 30, 31, 30, 31};
+    }
 
     private static int maximumDayInMonthFor(BigInteger year, int month) {
         if (month != DatatypeConstants.FEBRUARY) {
-            return daysInMonth[month];
+            return DaysInMonth.table[month];
         } else {
             if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
                     (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
@@ -2278,21 +2312,21 @@
                 // is a leap year.
                 return 29;
             } else {
-                return daysInMonth[month];
+                return DaysInMonth.table[month];
             }
         }
     }
 
     private static int maximumDayInMonthFor(int year, int month) {
         if (month != DatatypeConstants.FEBRUARY) {
-            return daysInMonth[month];
+            return DaysInMonth.table[month];
         } else {
             if (((year % 400) == 0) ||
                     (((year % 100) != 0) && ((year % 4) == 0))) {
                 // is a leap year.
                 return 29;
             } else {
-                return daysInMonth[DatatypeConstants.FEBRUARY];
+                return DaysInMonth.table[DatatypeConstants.FEBRUARY];
             }
         }
     }
@@ -2404,10 +2438,16 @@
         result.setGregorianChange(PURE_GREGORIAN_CHANGE);
 
         // if year( and eon) are undefined, leave default Calendar values
-        BigInteger year = getEonAndYear();
-        if (year != null) {
-            result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
-            result.set(Calendar.YEAR, year.abs().intValue());
+        if (year != DatatypeConstants.FIELD_UNDEFINED) {
+            if (eon == null) {
+                result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                result.set(Calendar.YEAR, Math.abs(year));
+            }
+            else {
+                BigInteger eonAndYear = getEonAndYear();
+                result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                result.set(Calendar.YEAR, eonAndYear.abs().intValue());
+            }
         }
 
         // only set month if it is set
@@ -2543,16 +2583,31 @@
         result.setGregorianChange(PURE_GREGORIAN_CHANGE);
 
         // if year( and eon) are undefined, leave default Calendar values
-        BigInteger year = getEonAndYear();
-        if (year != null) {
-            result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
-            result.set(Calendar.YEAR, year.abs().intValue());
+        if (year != DatatypeConstants.FIELD_UNDEFINED) {
+            if (eon == null) {
+                result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                result.set(Calendar.YEAR, Math.abs(year));
+            }
+            else {
+                final BigInteger eonAndYear = getEonAndYear();
+                result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                result.set(Calendar.YEAR, eonAndYear.abs().intValue());
+            }
         } else {
             // use default if set
-            BigInteger defaultYear = (defaults != null) ? defaults.getEonAndYear() : null;
-            if (defaultYear != null) {
-                result.set(Calendar.ERA, defaultYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
-                result.set(Calendar.YEAR, defaultYear.abs().intValue());
+            if (defaults != null) {
+                final int defaultYear = defaults.getYear();
+                if (defaultYear != DatatypeConstants.FIELD_UNDEFINED) {
+                    if (defaults.getEon() == null) {
+                        result.set(Calendar.ERA, defaultYear < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                        result.set(Calendar.YEAR, Math.abs(defaultYear));
+                    }
+                    else {
+                        final BigInteger defaultEonAndYear = defaults.getEonAndYear();
+                        result.set(Calendar.ERA, defaultEonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
+                        result.set(Calendar.YEAR, defaultEonAndYear.abs().intValue());
+                    }
+                }
             }
         }
 
@@ -2562,7 +2617,7 @@
             result.set(Calendar.MONTH, month - 1);
         } else {
             // use default if set
-            int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
+            final int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
             if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
                 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
                 result.set(Calendar.MONTH, defaultMonth - 1);
@@ -2574,7 +2629,7 @@
             result.set(Calendar.DAY_OF_MONTH, day);
         } else {
             // use default if set
-            int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
+            final int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
             if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
                 result.set(Calendar.DAY_OF_MONTH, defaultDay);
             }
@@ -2596,7 +2651,7 @@
             result.set(Calendar.MINUTE, minute);
         } else {
             // use default if set
-            int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
+            final int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
             if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
                 result.set(Calendar.MINUTE, defaultMinute);
             }
@@ -2607,7 +2662,7 @@
             result.set(Calendar.SECOND, second);
         } else {
             // use default if set
-            int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
+            final int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
             if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
                 result.set(Calendar.SECOND, defaultSecond);
             }
@@ -2618,7 +2673,7 @@
             result.set(Calendar.MILLISECOND, getMillisecond());
         } else {
             // use default if set
-            BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
+            final BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
             if (defaultFractionalSecond != null) {
                 result.set(Calendar.MILLISECOND, defaults.getMillisecond());
             }
@@ -2671,6 +2726,9 @@
             customTimezoneId.append(sign);
             customTimezoneId.append(hour);
             if (minutes != 0) {
+                if (minutes < 10) {
+                    customTimezoneId.append('0');
+                }
                 customTimezoneId.append(minutes);
             }
             result = TimeZone.getTimeZone(customTimezoneId.toString());
@@ -2718,7 +2776,7 @@
             if(millisecond<0 || 999<millisecond)
                 if(millisecond!=DatatypeConstants.FIELD_UNDEFINED)
                     invalidFieldValue(MILLISECOND, millisecond);
-            fractionalSecond = new BigDecimal((long) millisecond).movePointLeft(3);
+            fractionalSecond = BigDecimal.valueOf(millisecond, 3);
         }
     }
 
@@ -2770,7 +2828,7 @@
                 // seen meta character. we don't do error check against the format
                 switch (format.charAt(fidx++)) {
                     case 'Y' : // year
-                        parseAndSetYear(4);
+                        parseYear();
                         break;
 
                     case 'M' : // month
@@ -2851,7 +2909,7 @@
             int n = 0;
             char ch;
             int vstart = vidx;
-            while (isDigit(ch=peek()) && (vidx - vstart) <= maxDigits) {
+            while (isDigit(ch=peek()) && (vidx - vstart) < maxDigits) {
                 vidx++;
                 n = n*10 + ch-'0';
             }
@@ -2863,38 +2921,30 @@
             return n;
         }
 
-        private void parseAndSetYear(int minDigits)
-                throws IllegalArgumentException {
+        private void parseYear()
+            throws IllegalArgumentException {
             int vstart = vidx;
-            int n = 0;
-            boolean neg = false;
+            int sign = 0;
 
             // skip leading negative, if it exists
             if (peek() == '-') {
                 vidx++;
-                neg = true;
+                sign = 1;
             }
-            while(true) {
-                char ch = peek();
-                if(!isDigit(ch))
-                    break;
+            while (isDigit(peek())) {
                 vidx++;
-                n = n*10 + ch-'0';
             }
-
-            if ((vidx - vstart) < minDigits) {
+            final int digits = vidx - vstart - sign;
+            if (digits < 4) {
                 // we are expecting more digits
                 throw new IllegalArgumentException(value); //,vidx);
             }
-
-            if(vidx-vstart<7) {
-                // definitely int only. I don't know the exact # of digits that can be in int,
-                // but as long as we can catch (0-9999) range, that should be enough.
-                if(neg)     n = -n;
-                year = n;
-                eon = null;
-            } else {
-                setYear(new BigInteger(value.substring(vstart, vidx)));
+            final String yearString = value.substring(vstart, vidx);
+            if (digits < 10) {
+                setYear(Integer.parseInt(yearString));
+            }
+            else {
+                setYear(new BigInteger(yearString));
             }
         }
 
@@ -2922,150 +2972,123 @@
      * Prints this object according to the format specification.
      *
      * <p>
-     * I wrote a custom format method for a particular format string to
-     * see if it improves the performance, but it didn't. So this interpreting
-     * approach isn't too bad.
-     *
-     * <p>
      * StringBuffer -> StringBuilder change had a very visible impact.
-     * It almost cut the execution time to half, but unfortunately we can't use it
-     * because we need to run on JDK 1.3
+     * It almost cut the execution time to half.
+     * Diff from Xerces:
+     * Xerces use StringBuffer due to the requirement to support
+     * JDKs older than JDK 1.5
      */
     private String format( String format ) {
-        char[] buf = new char[32];
-        int bufPtr = 0;
-
+        StringBuilder buf = new StringBuilder();
         int fidx=0,flen=format.length();
 
         while(fidx<flen) {
             char fch = format.charAt(fidx++);
             if(fch!='%') {// not a meta char
-                buf[bufPtr++] = fch;
+                buf.append(fch);
                 continue;
             }
 
             switch(format.charAt(fidx++)) {
-            case 'Y':
-                if(eon==null) {
-                    // optimized path
-                    int y = getYear();
-                    if(y<0) {
-                        buf[bufPtr++] = '-';
-                        y = -y;
+                case 'Y':
+                    if (eon == null) {
+                        int absYear = year;
+                        if (absYear < 0) {
+                            buf.append('-');
+                            absYear = -year;
+                        }
+                        printNumber(buf, absYear, 4);
+                    }
+                    else {
+                        printNumber(buf, getEonAndYear(), 4);
                     }
-                    bufPtr = print4Number(buf,bufPtr,y);
-                } else {
-                    String s = getEonAndYear().toString();
-                    // reallocate the buffer now so that it has enough space
-                    char[] n = new char[buf.length+s.length()];
-                    System.arraycopy(buf,0,n,0,bufPtr);
-                    buf = n;
-                    for(int i=s.length();i<4;i++)
-                        buf[bufPtr++] = '0';
-                    s.getChars(0,s.length(),buf,bufPtr);
-                    bufPtr += s.length();
-                }
-                break;
-            case 'M':
-                bufPtr = print2Number(buf,bufPtr,getMonth());
-                break;
-            case 'D':
-                bufPtr = print2Number(buf,bufPtr,getDay());
-                break;
-            case 'h':
-                bufPtr = print2Number(buf,bufPtr,getHour());
-                break;
-            case 'm':
-                bufPtr = print2Number(buf,bufPtr,getMinute());
-                break;
-            case 's':
-                bufPtr = print2Number(buf,bufPtr,getSecond());
-                if (getFractionalSecond() != null) {
-                    // Note: toPlainString() isn't available before Java 1.5
-                    String frac = getFractionalSecond().toString();
-
-                    int pos = frac.indexOf("E-");
-                    if (pos >= 0) {
-                        String zeros = frac.substring(pos+2);
-                        frac = frac.substring(0,pos);
-                        pos = frac.indexOf(".");
-                        if (pos >= 0) {
-                            frac = frac.substring(0,pos) + frac.substring(pos+1);
+                    break;
+                case 'M':
+                    printNumber(buf,getMonth(),2);
+                    break;
+                case 'D':
+                    printNumber(buf,getDay(),2);
+                    break;
+                case 'h':
+                    printNumber(buf,getHour(),2);
+                    break;
+                case 'm':
+                    printNumber(buf,getMinute(),2);
+                    break;
+                case 's':
+                    printNumber(buf,getSecond(),2);
+                    if (getFractionalSecond() != null) {
+                        //Xerces uses a custom method toString instead of
+                        //toPlainString() since it needs to support JDKs older than 1.5
+                        String frac = getFractionalSecond().toPlainString();
+                        //skip leading zero.
+                        buf.append(frac.substring(1, frac.length()));
+                    }
+                    break;
+                case 'z':
+                    int offset = getTimezone();
+                    if (offset == 0) {
+                        buf.append('Z');
+                    }
+                    else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
+                        if (offset < 0) {
+                            buf.append('-');
+                            offset *= -1;
                         }
-                        int count = Integer.parseInt(zeros);
-                        if (count < 40) {
-                            frac = "00000000000000000000000000000000000000000".substring(0,count-1) + frac;
-                        } else {
-                            // do it the hard way
-                            while (count > 1) {
-                                frac = "0" + frac;
-                                count--;
-                            }
+                        else {
+                            buf.append('+');
                         }
-                        frac = "0." + frac;
+                        printNumber(buf,offset/60,2);
+                        buf.append(':');
+                        printNumber(buf,offset%60,2);
                     }
-
-                    // reallocate the buffer now so that it has enough space
-                    char[] n = new char[buf.length+frac.length()];
-                    System.arraycopy(buf,0,n,0,bufPtr);
-                    buf = n;
-                    //skip leading zero.
-                    frac.getChars(1, frac.length(), buf, bufPtr);
-                    bufPtr += frac.length()-1;
-                }
-                break;
-            case 'z':
-                int offset = getTimezone();
-                if (offset == 0) {
-                    buf[bufPtr++] = 'Z';
-                } else
-                if (offset != DatatypeConstants.FIELD_UNDEFINED) {
-                    if (offset < 0) {
-                        buf[bufPtr++] = '-';
-                        offset *= -1;
-                    } else {
-                        buf[bufPtr++] = '+';
-                    }
-                    bufPtr = print2Number(buf, bufPtr, offset / 60);
-                    buf[bufPtr++] = ':';
-                    bufPtr = print2Number(buf, bufPtr, offset % 60);
-                }
-                break;
-            default:
-                throw new InternalError();  // impossible
+                    break;
+                default:
+                    throw new InternalError();  // impossible
             }
         }
 
-        return new String(buf,0,bufPtr);
+        return buf.toString();
     }
 
     /**
-     * Prints an int as two digits into the buffer.
+     * Prints an integer as a String.
      *
+     * @param out
+     *      The formatted string will be appended into this buffer.
      * @param number
-     *      Number to be printed. Must be positive.
+     *      The integer to be printed.
+     * @param nDigits
+     *      The field will be printed by using at least this
+     *      number of digits. For example, 5 will be printed as "0005"
+     *      if nDigits==4.
      */
-    private int print2Number( char[] out, int bufptr, int number ) {
-        out[bufptr++] = (char) ('0'+(number/10));
-        out[bufptr++] = (char) ('0'+(number%10));
-        return bufptr;
+    private void printNumber( StringBuilder out, int number, int nDigits ) {
+        String s = String.valueOf(number);
+        for (int i = s.length(); i < nDigits; i++) {
+            out.append('0');
+        }
+        out.append(s);
     }
 
     /**
-     * Prints an int as four digits into the buffer.
+     * Prints an BigInteger as a String.
      *
+     * @param out
+     *      The formatted string will be appended into this buffer.
      * @param number
-     *      Number to be printed. Must be positive.
+     *      The integer to be printed.
+     * @param nDigits
+     *      The field will be printed by using at least this
+     *      number of digits. For example, 5 will be printed as "0005"
+     *      if nDigits==4.
      */
-    private int print4Number( char[] out, int bufptr, int number ) {
-        out[bufptr+3] = (char) ('0'+(number%10));
-        number /= 10;
-        out[bufptr+2] = (char) ('0'+(number%10));
-        number /= 10;
-        out[bufptr+1] = (char) ('0'+(number%10));
-        number /= 10;
-        out[bufptr  ] = (char) ('0'+(number%10));
-        return bufptr+4;
+    private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
+        String s = number.toString();
+        for (int i=s.length(); i < nDigits; i++) {
+            out.append('0');
+        }
+        out.append(s);
     }
 
     /**
@@ -3085,6 +3108,26 @@
      *  with the creation of new <code>XMLGregorianCalendar</code>s.</p>
      */
     public void reset() {
-        //PENDING : Implementation of reset method
+        eon = orig_eon;
+        year = orig_year;
+        month = orig_month;
+        day = orig_day;
+        hour = orig_hour;
+        minute = orig_minute;
+        second = orig_second;
+        fractionalSecond = orig_fracSeconds;
+        timezone = orig_timezone;
     }
+
+    /** Deserialize Calendar. */
+    private void readObject(ObjectInputStream ois)
+        throws ClassNotFoundException, IOException {
+
+        // perform default deseralization
+        ois.defaultReadObject();
+
+        // initialize orig_* fields
+        save();
+
+    } // readObject(ObjectInputStream)
 }