1/*
2 * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.org.apache.xerces.internal.jaxp.datatype;
27
28import java.io.IOException;
29import java.io.ObjectInputStream;
30import java.io.Serializable;
31import java.math.BigDecimal;
32import java.math.BigInteger;
33import java.util.TimeZone;
34import java.util.Calendar;
35import java.util.GregorianCalendar;
36import java.util.Date;
37import java.util.Locale;
38
39import javax.xml.datatype.DatatypeConstants;
40import javax.xml.datatype.Duration;
41import javax.xml.datatype.XMLGregorianCalendar;
42import javax.xml.namespace.QName;
43import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
44import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
45
46/**
47 * <p>Representation for W3C XML Schema 1.0 date/time datatypes.
48 * Specifically, these date/time datatypes are
49 * {@link DatatypeConstants#DATETIME dateTime},
50 * {@link DatatypeConstants#TIME time},
51 * {@link DatatypeConstants#DATE date},
52 * {@link DatatypeConstants#GYEARMONTH gYearMonth},
53 * {@link DatatypeConstants#GMONTHDAY gMonthDay},
54 * {@link DatatypeConstants#GYEAR gYear},
55 * {@link DatatypeConstants#GMONTH gMonth} and
56 * {@link DatatypeConstants#GDAY gDay}
57 * defined in the XML Namespace
58 * <code>"http://www.w3.org/2001/XMLSchema"</code>.
59 * These datatypes are normatively defined in
60 * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML Schema 1.0 Part 2, Section 3.2.7-14</a>.</p>
61 *
62 * <p>The table below defines the mapping between XML Schema 1.0
63 * date/time datatype fields and this class' fields. It also summarizes
64 * the value constraints for the date and time fields defined in
65 * <a href="http://www.w3.org/TR/xmlschema-2/#isoformats">W3C XML Schema 1.0 Part 2, Appendix D,
66 * <i>ISO 8601 Date and Time Formats</i></a>.</p>
67 *
68 * <a name="datetimefieldsmapping"/>
69 * <table border="2" rules="all" cellpadding="2">
70 *   <thead>
71 *     <tr>
72 *       <th align="center" colspan="3">
73 *         Date/time datatype field mapping between XML Schema 1.0 and Java representation
74 *       </th>
75 *     </tr>
76 *   </thead>
77 *   <tbody>
78 *     <tr>
79 *       <th>XML Schema 1.0<br/>
80 *           datatype<br/>
81 *            field</th>
82 *       <th>Related<br/>XMLGregorianCalendar<br/>Accessor(s)</th>
83 *       <th>Value Range</th>
84 *     </tr>
85 *     <a name="datetimefield-year"/>
86 *     <tr>
87 *       <td> year </td>
88 *       <td> {@link #getYear()} + {@link #getEon()} or<br/>
89 *            {@link #getEonAndYear}
90 *       </td>
91 *       <td> <code>getYear()</code> is a value between -(10^9-1) to (10^9)-1
92 *            or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
93 *            {@link #getEon()} is high order year value in billion of years.<br/>
94 *            <code>getEon()</code> has values greater than or equal to (10^9) or less than or equal to -(10^9).
95 *            A value of null indicates field is undefined.</br>
96 *            Given that <a href="http://www.w3.org/2001/05/xmlschema-errata#e2-63">XML Schema 1.0 errata</a> states that the year zero
97 *            will be a valid lexical value in a future version of XML Schema,
98 *            this class allows the year field to be set to zero. Otherwise,
99 *            the year field value is handled exactly as described
100 *            in the errata and [ISO-8601-1988]. Note that W3C XML Schema 1.0
101 *            validation does not allow for the year field to have a value of zero.
102 *            </td>
103 *     </tr>
104 *     <a name="datetimefield-month"/>
105 *     <tr>
106 *       <td> month </td>
107 *       <td> {@link #getMonth()} </td>
108 *       <td> 1 to 12 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
109 *     </tr>
110 *     <a name="datetimefield-day"/>
111 *     <tr>
112 *       <td> day </td>
113 *       <td> {@link #getDay()} </td>
114 *       <td> Independent of month, max range is 1 to 31 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
115 *            The normative value constraint stated relative to month
116 *            field's value is in <a href="http://www.w3.org/TR/xmlschema-2/#isoformats">W3C XML Schema 1.0 Part 2, Appendix D</a>.
117 *       </td>
118 *     </tr>
119 *     <a name="datetimefield-hour"/>
120 *     <tr>
121 *       <td> hour </td>
122 *       <td> {@link #getHour()} </td>
123 *       <td>
124 *         0 to 23 or {@link DatatypeConstants#FIELD_UNDEFINED}.
125 *         An hour value of 24 is allowed to be set in the lexical space provided the minute and second
126 *         field values are zero. However, an hour value of 24 is not allowed in value space and will be
127 *         transformed to represent the value of the first instance of the following day as per
128 *         <a href="http://www.w3.org/TR/xmlschema-2/#built-in-primitive-datatypes">
129 *         XML Schema Part 2: Datatypes Second Edition, 3.2 Primitive datatypes</a>.
130 *       </td>
131 *     </tr>
132 *     <a name="datetimefield-minute"/>
133 *     <tr>
134 *       <td> minute </td>
135 *       <td> {@link #getMinute()} </td>
136 *       <td> 0 to 59 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
137 *     </tr>
138 *     <a name="datetimefield-second"/>
139 *     <tr>
140 *       <td>second</td>
141 *       <td>
142 *         {@link #getSecond()} + {@link #getMillisecond()}/1000 or<br/>
143 *         {@link #getSecond()} + {@link #getFractionalSecond()}
144 *       </td>
145 *       <td>
146 *         {@link #getSecond()} from 0 to 60 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
147 *         <i>(Note: 60 only allowable for leap second.)</i><br/>
148 *         {@link #getFractionalSecond()} allows for infinite precision over the range from 0.0 to 1.0 when
149 *         the {@link #getSecond()} is defined.<br/>
150 *         <code>FractionalSecond</code> is optional and has a value of <code>null</code> when it is undefined.<br />
151 *            {@link #getMillisecond()} is the convenience
152 *            millisecond precision of value of {@link #getFractionalSecond()}.
153 *       </td>
154 *     </tr>
155 *     <tr id="datetimefield-timezone">
156 *       <td> timezone </td>
157 *       <td> {@link #getTimezone()} </td>
158 *       <td> Number of minutes or {@link DatatypeConstants#FIELD_UNDEFINED}.
159 *         Value range from -14 hours (-14 * 60 minutes) to 14 hours (14 * 60 minutes).
160 *       </td>
161 *     </tr>
162 *   </tbody>
163 *  </table>
164 *
165 * <p>All maximum value space constraints listed for the fields in the table
166 * above are checked by factory methods, setter methods and parse methods of
167 * this class. <code>IllegalArgumentException</code> is thrown when
168 * parameter's value is outside the maximum value constraint for the field.
169 * Validation checks, for example, whether days in month should be
170 * limited to 29, 30 or 31 days, that are dependent on the values of other
171 * fields are not checked by these methods.
172 * </p>
173 *
174 * <p>The following operations are defined for this class:
175 * <ul>
176 *   <li>factory methods to create instances</li>
177 *   <li>accessors/mutators for independent date/time fields</li>
178 *   <li>conversion between this class and W3C XML Schema 1.0 lexical representation</li>
179 *   <li>conversion between this class and <code>java.util.GregorianCalendar</code></li>
180 *   <li>partial order relation comparator method, {@link #compare(XMLGregorianCalendar)}</li>
181 *   <li>{@link #equals(Object)} defined relative to {@link #compare(XMLGregorianCalendar)}.</li>
182 *   <li> addition operation with {@link javax.xml.datatype.Duration}.
183 * instance as defined in <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">
184 * W3C XML Schema 1.0 Part 2, Appendix E, <i>Adding durations to dateTimes</i></a>.</li>
185 * </ul>
186 * </p>
187 *
188 * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
189 * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
190 * @author <a href="mailto:Sunitha.Reddy@Sun.com">Sunitha Reddy</a>
191 * @see javax.xml.datatype.Duration
192 * @since 1.5
193 */
194
195public class XMLGregorianCalendarImpl
196        extends XMLGregorianCalendar
197        implements Serializable, Cloneable {
198
199    /** Backup values **/
200    transient private BigInteger orig_eon;
201    transient private int orig_year = DatatypeConstants.FIELD_UNDEFINED;
202    transient private int orig_month = DatatypeConstants.FIELD_UNDEFINED;
203    transient private int orig_day = DatatypeConstants.FIELD_UNDEFINED;
204    transient private int orig_hour = DatatypeConstants.FIELD_UNDEFINED;
205    transient private int orig_minute = DatatypeConstants.FIELD_UNDEFINED;
206    transient private int orig_second = DatatypeConstants.FIELD_UNDEFINED;
207    transient private BigDecimal orig_fracSeconds;
208    transient private int orig_timezone = DatatypeConstants.FIELD_UNDEFINED;
209
210    /**
211     * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
212     */
213    private BigInteger eon = null;
214
215    /**
216     * <p>Year of this <code>XMLGregorianCalendar</code>.</p>
217     */
218    private int year = DatatypeConstants.FIELD_UNDEFINED;
219
220    /**
221     * <p>Month of this <code>XMLGregorianCalendar</code>.</p>
222     */
223    private int month = DatatypeConstants.FIELD_UNDEFINED;
224
225    /**
226     * <p>Day of this <code>XMLGregorianCalendar</code>.</p>
227     */
228    private int day = DatatypeConstants.FIELD_UNDEFINED;
229
230    /**
231     * <p>Timezone of this <code>XMLGregorianCalendar</code> in minutes.</p>
232     */
233    private int timezone = DatatypeConstants.FIELD_UNDEFINED;
234
235    /**
236     * <p>Hour of this <code>XMLGregorianCalendar</code>.</p>
237     */
238    private int hour = DatatypeConstants.FIELD_UNDEFINED;
239
240    /**
241     * <p>Minute of this <code>XMLGregorianCalendar</code>.</p>
242     */
243    private int minute = DatatypeConstants.FIELD_UNDEFINED;
244
245    /**
246     * <p>Second of this <code>XMLGregorianCalendar</code>.</p>
247     */
248    private int second = DatatypeConstants.FIELD_UNDEFINED ;
249
250    /**
251     * <p>Fractional second of this <code>XMLGregorianCalendar</code>.</p>
252     */
253    private BigDecimal fractionalSecond = null;
254
255    /**
256     * <p>BigInteger constant; representing a billion.</p>
257     */
258    private static final BigInteger BILLION_B = new BigInteger("1000000000");
259
260    /**
261     * <p>int constant; representing a billion.</p>
262     */
263    private static final int BILLION_I = 1000000000;
264
265    /**
266     *   <p>Obtain a pure Gregorian Calendar by calling
267     *   GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE). </p>
268     */
269    private static final Date PURE_GREGORIAN_CHANGE =
270        new Date(Long.MIN_VALUE);
271
272    /**
273     * Year index for MIN_ and MAX_FIELD_VALUES.
274     */
275    private static final int YEAR   = 0;
276
277    /**
278     * Month index for MIN_ and MAX_FIELD_VALUES.
279     */
280    private static final int MONTH  = 1;
281
282    /**
283     * Day index for MIN_ and MAX_FIELD_VALUES.
284     */
285    private static final int DAY    = 2;
286
287    /**
288     * Hour index for MIN_ and MAX_FIELD_VALUES.
289     */
290    private static final int HOUR   = 3;
291
292    /**
293     * Minute index for MIN_ and MAX_FIELD_VALUES.
294     */
295    private static final int MINUTE = 4;
296
297    /**
298     * Second index for MIN_ and MAX_FIELD_VALUES.
299     */
300    private static final int SECOND = 5;
301
302    /**
303     * Second index for MIN_ and MAX_FIELD_VALUES.
304     */
305    private static final int MILLISECOND = 6;
306
307    /**
308     * Timezone index for MIN_ and MAX_FIELD_VALUES
309     */
310    private static final int TIMEZONE = 7;
311
312
313    /**
314     * field names indexed by YEAR..TIMEZONE.
315     */
316    private static final String FIELD_NAME[] = {
317        "Year",
318        "Month",
319        "Day",
320        "Hour",
321        "Minute",
322        "Second",
323        "Millisecond",
324        "Timezone"
325    };
326
327    /**
328     * <p>Stream Unique Identifier.</p>
329     *
330     * <p>TODO: Serialization should use the XML string representation as
331     * the serialization format to ensure future compatibility.</p>
332     */
333    private static final long serialVersionUID = 1L;
334
335    /**
336     * <p>Use as a template for default field values when
337     * converting to a {@link GregorianCalendar}, set to a leap
338     * year date of January 1, 0400 at midnight.</p>
339     *
340     * <p>Fields that are optional for an <code>xsd:dateTime</code> instances are defaulted to not being set to any value.
341     * <code>XMLGregorianCalendar</code> fields millisecond, fractional second and timezone return the value indicating
342     * that the field is not set, {@link DatatypeConstants#FIELD_UNDEFINED} for millisecond and timezone
343     * and <code>null</code> for fractional second.</p>
344     *
345     * @see #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
346     */
347    public static final XMLGregorianCalendar LEAP_YEAR_DEFAULT =
348                createDateTime(
349                        400,  //year
350                DatatypeConstants.JANUARY,  //month
351                        1,  // day
352                    0,  // hour
353                    0,  // minute
354                    0,  // second
355                    DatatypeConstants.FIELD_UNDEFINED,  // milliseconds
356                    DatatypeConstants.FIELD_UNDEFINED // timezone
357                );
358
359    // Constructors
360
361    /**
362     * Constructs a new XMLGregorianCalendar object.
363     *
364     * String parsing documented by {@link #parse(String)}.
365     *
366     * Returns a non-null valid XMLGregorianCalendar object that holds the
367     * value indicated by the lexicalRepresentation parameter.
368     *
369     * @param lexicalRepresentation
370     *      Lexical representation of one the eight
371     *      XML Schema date/time datatypes.
372     * @throws IllegalArgumentException
373     *      If the given string does not conform as documented in
374     *      {@link #parse(String)}.
375     * @throws NullPointerException
376     *      If the given string is null.
377     */
378    protected XMLGregorianCalendarImpl(String lexicalRepresentation)
379            throws IllegalArgumentException {
380
381        // compute format string for this lexical representation.
382        String format = null;
383        String lexRep = lexicalRepresentation;
384        final int NOT_FOUND = -1;
385        int lexRepLength = lexRep.length();
386
387        // current parser needs a format string,
388        // use following heuristics to figure out what xml schema date/time
389        // datatype this lexical string could represent.
390        // Fix 4971612: invalid SCCS macro substitution in data string,
391        //   no %{alpha}% to avoid SCCS maco substitution
392        if (lexRep.indexOf('T') != NOT_FOUND) {
393            // found Date Time separater, must be xsd:DateTime
394            format = "%Y-%M-%DT%h:%m:%s" + "%z";
395        } else if (lexRepLength >= 3 && lexRep.charAt(2) == ':') {
396            // found ":", must be xsd:Time
397            format = "%h:%m:%s" + "%z";
398        } else if (lexRep.startsWith("--")) {
399            // check for gDay || gMonth || gMonthDay
400            if (lexRepLength >= 3 && lexRep.charAt(2) == '-') {
401                // gDay, ---DD(z?)
402                format = "---%D" + "%z";
403            } else if (lexRepLength == 4     // --MM
404                    || lexRepLength == 5     // --MMZ
405                    || lexRepLength == 10) { // --MMSHH:MM
406                // gMonth, --MM(z?),
407                // per XML Schema Errata, used to be --MM--(z?)
408                format = "--%M" + "%z";
409            } else {
410                // gMonthDay, --MM-DD(z?), (or invalid lexicalRepresentation)
411                // length should be:
412                //  7: --MM-DD
413                //  8: --MM-DDZ
414                // 13: --MM-DDSHH:MM
415                format = "--%M-%D" + "%z";
416            }
417        } else {
418            // check for Date || GYear | GYearMonth
419            int countSeparator = 0;
420
421            // start at index 1 to skip potential negative sign for year.
422
423
424            int timezoneOffset = lexRep.indexOf(':');
425            if (timezoneOffset != NOT_FOUND) {
426
427                // found timezone, strip it off for distinguishing
428                // between Date, GYear and GYearMonth so possible
429                // negative sign in timezone is not mistaken as
430                // a separator.
431                lexRepLength -= 6;
432            }
433
434            for (int i = 1; i < lexRepLength; i++) {
435                if (lexRep.charAt(i) == '-') {
436                    countSeparator++;
437                }
438            }
439            if (countSeparator == 0) {
440                // GYear
441                format = "%Y" + "%z";
442            } else if (countSeparator == 1) {
443                // GYearMonth
444                format = "%Y-%M" + "%z";
445            } else {
446                // Date or invalid lexicalRepresentation
447                // Fix 4971612: invalid SCCS macro substitution in data string
448                format = "%Y-%M-%D" + "%z";
449            }
450        }
451        Parser p = new Parser(format, lexRep);
452        p.parse();
453
454        // check for validity
455        if (!isValid()) {
456            throw new IllegalArgumentException(
457                    DatatypeMessageFormatter.formatMessage(null, "InvalidXGCRepresentation", new Object[]{lexicalRepresentation})
458                    //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
459            );
460        }
461
462        save();
463    }
464
465    /**
466     * save original values
467     */
468    private void save() {
469        orig_eon = eon;
470        orig_year = year;
471        orig_month = month;
472        orig_day = day;
473        orig_hour = hour;
474        orig_minute = minute;
475        orig_second = second;
476        orig_fracSeconds = fractionalSecond;
477        orig_timezone = timezone;
478    }
479
480    /**
481     * <p>Create an instance with all date/time datatype fields set to
482     * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.</p>
483     */
484    public XMLGregorianCalendarImpl() {
485
486        // field initializers already do the correct initialization.
487    }
488
489    /**
490     * <p>Private constructor allowing for complete value spaces allowed by
491     * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
492     * builtin datatypes. Note that <code>year</code> parameter supports
493     * arbitrarily large numbers and fractionalSecond has infinite
494     * precision.</p>
495     *
496     * @param year of <code>XMLGregorianCalendar</code> to be created.
497     * @param month of <code>XMLGregorianCalendar</code> to be created.
498     * @param day of <code>XMLGregorianCalendar</code> to be created.
499     * @param hour of <code>XMLGregorianCalendar</code> to be created.
500     * @param minute of <code>XMLGregorianCalendar</code> to be created.
501     * @param second of <code>XMLGregorianCalendar</code> to be created.
502     * @param fractionalSecond of <code>XMLGregorianCalendar</code> to be created.
503     * @param timezone of <code>XMLGregorianCalendar</code> to be created.
504     *
505     */
506    protected XMLGregorianCalendarImpl(
507        BigInteger year,
508        int month,
509        int day,
510        int hour,
511        int minute,
512        int second,
513        BigDecimal fractionalSecond,
514        int timezone) {
515
516        setYear(year);
517        setMonth(month);
518        setDay(day);
519        setTime(hour, minute, second, fractionalSecond);
520        setTimezone(timezone);
521
522        // check for validity
523        if (!isValid()) {
524
525            throw new IllegalArgumentException(
526                DatatypeMessageFormatter.formatMessage(null,
527                    "InvalidXGCValue-fractional",
528                    new Object[] { year, new Integer(month), new Integer(day),
529                    new Integer(hour), new Integer(minute), new Integer(second),
530                    fractionalSecond, new Integer(timezone)})
531                        );
532
533                        /**
534                String yearString = "null";
535                if (year != null) {
536                    yearString = year.toString();
537                }
538                String fractionalSecondString = "null";
539                if (fractionalSecond != null) {
540                    fractionalSecondString = fractionalSecond.toString();
541                }
542
543                throw new IllegalArgumentException(
544                    "year = " + yearString
545                    + ", month = " + month
546                    + ", day = " + day
547                    + ", hour = " + hour
548                    + ", minute = " + minute
549                    + ", second = " + second
550                    + ", fractionalSecond = " + fractionalSecondString
551                    + ", timezone = " + timezone
552                    + ", is not a valid representation of an XML Gregorian Calendar value."
553                );
554                */
555
556        }
557
558        save();
559    }
560
561    /**
562     * <p>Private constructor of value spaces that a
563     * <code>java.util.GregorianCalendar</code> instance would need to convert to an
564     * <code>XMLGregorianCalendar</code> instance.</p>
565     *
566     * <p><code>XMLGregorianCalendar eon</code> and
567     * <code>fractionalSecond</code> are set to <code>null</code></p>
568     *
569     * @param year of <code>XMLGregorianCalendar</code> to be created.
570     * @param month of <code>XMLGregorianCalendar</code> to be created.
571     * @param day of <code>XMLGregorianCalendar</code> to be created.
572     * @param hour of <code>XMLGregorianCalendar</code> to be created.
573     * @param minute of <code>XMLGregorianCalendar</code> to be created.
574     * @param second of <code>XMLGregorianCalendar</code> to be created.
575     * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
576     * @param timezone of <code>XMLGregorianCalendar</code> to be created.
577     */
578    private XMLGregorianCalendarImpl(
579        int year,
580        int month,
581        int day,
582        int hour,
583        int minute,
584        int second,
585        int millisecond,
586        int timezone) {
587
588        setYear(year);
589        setMonth(month);
590        setDay(day);
591        setTime(hour, minute, second);
592        setTimezone(timezone);
593        BigDecimal realMilliseconds = null;
594        if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
595            realMilliseconds = BigDecimal.valueOf(millisecond, 3);
596        }
597        setFractionalSecond(realMilliseconds);
598
599        if (!isValid()) {
600
601            throw new IllegalArgumentException(
602                DatatypeMessageFormatter.formatMessage(null,
603                "InvalidXGCValue-milli",
604                new Object[] { new Integer(year), new Integer(month), new Integer(day),
605                new Integer(hour), new Integer(minute), new Integer(second),
606                new Integer(millisecond), new Integer(timezone)})
607                        );
608                /*
609                throw new IllegalArgumentException(
610                    "year = " + year
611                    + ", month = " + month
612                    + ", day = " + day
613                    + ", hour = " + hour
614                    + ", minute = " + minute
615                    + ", second = " + second
616                    + ", millisecond = " + millisecond
617                    + ", timezone = " + timezone
618                    + ", is not a valid representation of an XML Gregorian Calendar value."
619                    );
620                 */
621
622        }
623
624        save();
625    }
626
627        /**
628         * <p>Convert a <code>java.util.GregorianCalendar</code> to XML Schema 1.0
629         * representation.</p>
630         *
631         * <table border="2" rules="all" cellpadding="2">
632         *   <thead>
633         *     <tr>
634         *       <th align="center" colspan="2">
635         *          Field by Field Conversion from
636         *          <code>java.util.GregorianCalendar</code> to this class
637         *       </th>
638         *     </tr>
639         *   </thead>
640         *   <tbody>
641         *     <tr>
642         *        <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
643         *        <th><code>java.util.GregorianCalendar</code> field</th>
644         *     </tr>
645         *     <tr>
646         *       <th>{@link #setYear(int)}</th>
647         *       <th><code>ERA == GregorianCalendar.BC ? -YEAR : YEAR</code></th>
648         *     </tr>
649         *     <tr>
650         *       <th>{@link #setMonth(int)}</th>
651         *       <th><code>MONTH + 1</code></th>
652         *     </tr>
653         *     <tr>
654         *       <th>{@link #setDay(int)}</th>
655         *       <th><code>DAY_OF_MONTH</code></th>
656         *     </tr>
657         *     <tr>
658         *       <th>{@link #setTime(int,int,int, BigDecimal)}</th>
659         *       <th><code>HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND</code></th>
660         *     </tr>
661         *     <tr>
662         *       <th>{@link #setTimezone(int)}<i>*</i></th>
663         *       <th><code>(ZONE_OFFSET + DST_OFFSET) / (60*1000)</code><br/>
664         *       <i>(in minutes)</i>
665         *       </th>
666         *     </tr>
667         *   </tbody>
668         * </table>
669         * <p><i>*</i>conversion loss of information. It is not possible to represent
670         * a <code>java.util.GregorianCalendar</code> daylight savings timezone id in the
671         * XML Schema 1.0 date/time datatype representation.</p>
672         *
673         * <p>To compute the return value's <code>TimeZone</code> field,
674         * <ul>
675         * <li>when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
676         * create a <code>java.util.TimeZone</code> with a custom timezone id
677         * using the <code>this.getTimezone()</code>.</li>
678         * <li>else use the <code>GregorianCalendar</code> default timezone value
679         * for the host is defined as specified by
680         * <code>java.util.TimeZone.getDefault()</code>.</li></p>
681         *
682         * @param cal <code>java.util.GregorianCalendar</code> used to create <code>XMLGregorianCalendar</code>
683         */
684    public XMLGregorianCalendarImpl(GregorianCalendar cal) {
685
686        int year = cal.get(Calendar.YEAR);
687        if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
688            year = -year;
689        }
690        this.setYear(year);
691
692        // Calendar.MONTH is zero based, XSD Date datatype's month field starts
693        // with JANUARY as 1.
694        this.setMonth(cal.get(Calendar.MONTH) + 1);
695        this.setDay(cal.get(Calendar.DAY_OF_MONTH));
696        this.setTime(
697                cal.get(Calendar.HOUR_OF_DAY),
698                cal.get(Calendar.MINUTE),
699                cal.get(Calendar.SECOND),
700                cal.get(Calendar.MILLISECOND));
701
702        // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
703        int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
704        this.setTimezone(offsetInMinutes);
705        save();
706    }
707
708    // Factories
709
710    /**
711     * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
712     * All possible fields are specified for this factory method.</p>
713     *
714     * @param year represents both high-order eons and low-order year.
715     * @param month of <code>dateTime</code>
716     * @param day of <code>dateTime</code>
717     * @param hours of <code>dateTime</code>
718     * @param minutes of <code>dateTime</code>
719     * @param seconds of <code>dateTime</code>
720     * @param fractionalSecond value of null indicates optional field is absent.
721     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
722     *
723     * @return <code>XMLGregorianCalendar</code> created from parameter values.
724     *
725     * @see DatatypeConstants#FIELD_UNDEFINED
726     *
727     * @throws IllegalArgumentException if any parameter is outside value
728     * constraints for the field as specified in
729     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
730     */
731    public static XMLGregorianCalendar createDateTime(
732        BigInteger year,
733        int month,
734        int day,
735        int hours,
736        int minutes,
737        int seconds,
738        BigDecimal fractionalSecond,
739        int timezone) {
740
741        return new XMLGregorianCalendarImpl(
742            year,
743            month,
744            day,
745            hours,
746            minutes,
747            seconds,
748            fractionalSecond,
749            timezone);
750    }
751
752    /**
753     * <p>Create a Java instance of XML Schema builtin datatype dateTime.</p>
754     *
755     * @param year represents both high-order eons and low-order year.
756     * @param month of <code>dateTime</code>
757     * @param day of <code>dateTime</code>
758     * @param hour of <code>dateTime</code>
759     * @param minute of <code>dateTime</code>
760     * @param second of <code>dateTime</code>
761     *
762     * @return <code>XMLGregorianCalendar</code> created from parameter values.
763     *
764     * @throws IllegalArgumentException if any parameter is outside value constraints for the field as specified in
765     *   <a href="#datetimefieldmapping">date/time field mapping table</a>.
766     *
767     * @see DatatypeConstants#FIELD_UNDEFINED
768     */
769    public static XMLGregorianCalendar createDateTime(
770        int year,
771        int month,
772        int day,
773        int hour,
774        int minute,
775        int second) {
776
777        return new XMLGregorianCalendarImpl(
778            year,
779            month,
780            day,
781            hour,
782            minute,
783            second,
784            DatatypeConstants.FIELD_UNDEFINED,  //millisecond
785                DatatypeConstants.FIELD_UNDEFINED //timezone
786        );
787    }
788
789    /**
790     * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
791     * All possible fields are specified for this factory method.</p>
792     *
793     * @param year represents low-order year.
794     * @param month of <code>dateTime</code>
795     * @param day of <code>dateTime</code>
796     * @param hours of <code>dateTime</code>
797     * @param minutes of <code>dateTime</code>
798     * @param seconds of <code>dateTime</code>
799     * @param milliseconds of <code>dateTime</code>. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
800     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
801     *
802     * @return <code>XMLGregorianCalendar</code> created from parameter values.
803     *
804     * @throws IllegalArgumentException if any parameter is outside value constraints for the field as specified in
805     *   <a href="#datetimefieldmapping">date/time field mapping table</a>.
806     *
807     * @see DatatypeConstants#FIELD_UNDEFINED
808     */
809    public static XMLGregorianCalendar createDateTime(
810        int year,
811        int month,
812        int day,
813        int hours,
814        int minutes,
815        int seconds,
816        int milliseconds,
817        int timezone) {
818
819        return new XMLGregorianCalendarImpl(
820            year,
821            month,
822            day,
823            hours,
824            minutes,
825            seconds,
826            milliseconds,
827            timezone);
828    }
829
830    /**
831     * <p>Create a Java representation of XML Schema builtin datatype <code>date</code> or <code>g*</code>.</p>
832     *
833     * <p>For example, an instance of <code>gYear</code> can be created invoking this factory
834     * with <code>month</code> and <code>day</code> parameters set to
835     * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
836     *
837     * @param year of <code>XMLGregorianCalendar</code> to be created.
838     * @param month of <code>XMLGregorianCalendar</code> to be created.
839     * @param day of <code>XMLGregorianCalendar</code> to be created.
840     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
841     *
842     * @return <code>XMLGregorianCalendar</code> created from parameter values.
843     *
844     * @see DatatypeConstants#FIELD_UNDEFINED
845     *
846     * @throws IllegalArgumentException if any parameter is outside value
847     * constraints for the field as specified in
848     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
849     */
850    public static XMLGregorianCalendar createDate(
851        int year,
852        int month,
853        int day,
854        int timezone) {
855
856        return new XMLGregorianCalendarImpl(
857            year,
858            month,
859            day,
860            DatatypeConstants.FIELD_UNDEFINED, // hour
861            DatatypeConstants.FIELD_UNDEFINED, // minute
862            DatatypeConstants.FIELD_UNDEFINED, // second
863                DatatypeConstants.FIELD_UNDEFINED, // millisecond
864            timezone);
865    }
866
867    /**
868     * Create a Java instance of XML Schema builtin datatype <code>time</code>.
869     * @param hours number of hours
870     * @param minutes number of minutes
871     * @param seconds number of seconds
872     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
873     *
874     * @return <code>XMLGregorianCalendar</code> created from parameter values.
875     *
876     * @see DatatypeConstants#FIELD_UNDEFINED
877     *
878     * @throws IllegalArgumentException if any parameter is outside value
879     * constraints for the field as specified in
880     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
881     */
882    public static XMLGregorianCalendar createTime(
883        int hours,
884        int minutes,
885        int seconds,
886                int timezone) {
887
888                return new XMLGregorianCalendarImpl(
889                        DatatypeConstants.FIELD_UNDEFINED, // Year
890                        DatatypeConstants.FIELD_UNDEFINED, // Month
891                        DatatypeConstants.FIELD_UNDEFINED, // Day
892                        hours,
893                        minutes,
894                        seconds,
895                        DatatypeConstants.FIELD_UNDEFINED, //Millisecond
896                        timezone);
897    }
898
899    /**
900     * <p>Create a Java instance of XML Schema builtin datatype time.</p>
901     *
902     * @param hours number of hours
903     * @param minutes number of minutes
904     * @param seconds number of seconds
905     * @param fractionalSecond value of <code>null</code> indicates that this optional field is not set.
906     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
907     *
908     * @return <code>XMLGregorianCalendar</code> created from parameter values.
909     *
910     * @see DatatypeConstants#FIELD_UNDEFINED
911     *
912     * @throws IllegalArgumentException if any parameter is outside value
913     * constraints for the field as specified in
914     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
915     */
916    public static XMLGregorianCalendar createTime(
917        int hours,
918        int minutes,
919        int seconds,
920        BigDecimal fractionalSecond,
921        int timezone) {
922
923        return new XMLGregorianCalendarImpl(
924            null,            // Year
925            DatatypeConstants.FIELD_UNDEFINED, // month
926            DatatypeConstants.FIELD_UNDEFINED, // day
927            hours,
928            minutes,
929            seconds,
930            fractionalSecond,
931            timezone);
932    }
933
934    /**
935     * <p>Create a Java instance of XML Schema builtin datatype time.</p>
936     *
937     * @param hours number of hours
938     * @param minutes number of minutes
939     * @param seconds number of seconds
940     * @param milliseconds number of milliseconds
941     * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
942     *
943     * @return <code>XMLGregorianCalendar</code> created from parameter values.
944     *
945     * @see DatatypeConstants#FIELD_UNDEFINED
946     *
947     * @throws IllegalArgumentException if any parameter is outside value
948     * constraints for the field as specified in
949     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
950     */
951    public static XMLGregorianCalendar createTime(
952        int hours,
953        int minutes,
954        int seconds,
955        int milliseconds,
956        int timezone) {
957
958        return new XMLGregorianCalendarImpl(
959                DatatypeConstants.FIELD_UNDEFINED, // year
960                DatatypeConstants.FIELD_UNDEFINED, // month
961                DatatypeConstants.FIELD_UNDEFINED, // day
962                hours,
963                minutes,
964                seconds,
965                milliseconds,
966                timezone);
967    }
968
969    // Accessors
970
971    /**
972     * <p>Return high order component for XML Schema 1.0 dateTime datatype field for
973     * <code>year</code>.
974     * <code>null</code> if this optional part of the year field is not defined.</p>
975     *
976     * <p>Value constraints for this value are summarized in
977     * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
978     * @return eon of this <code>XMLGregorianCalendar</code>. The value
979     * returned is an integer multiple of 10^9.
980     *
981     * @see #getYear()
982     * @see #getEonAndYear()
983     */
984    public BigInteger getEon() {
985           return eon;
986    }
987
988    /**
989     * <p>Return low order component for XML Schema 1.0 dateTime datatype field for
990     * <code>year</code> or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
991     *
992     * <p>Value constraints for this value are summarized in
993     * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
994     *
995     * @return year  of this <code>XMLGregorianCalendar</code>.
996     *
997     * @see #getEon()
998     * @see #getEonAndYear()
999     */
1000    public int getYear() {
1001           return year;
1002    }
1003
1004    /**
1005     * <p>Return XML Schema 1.0 dateTime datatype field for
1006     * <code>year</code>.</p>
1007     *
1008     * <p>Value constraints for this value are summarized in
1009     * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
1010     *
1011     * @return sum of <code>eon</code> and <code>BigInteger.valueOf(year)</code>
1012     * when both fields are defined. When only <code>year</code> is defined,
1013     * return it. When both <code>eon</code> and <code>year</code> are not
1014     * defined, return <code>null</code>.
1015     *
1016     * @see #getEon()
1017     * @see #getYear()
1018     */
1019    public BigInteger getEonAndYear() {
1020
1021                // both are defined
1022                if (year != DatatypeConstants.FIELD_UNDEFINED
1023                        && eon != null) {
1024
1025                        return eon.add(BigInteger.valueOf((long) year));
1026                }
1027
1028                // only year is defined
1029                if (year != DatatypeConstants.FIELD_UNDEFINED
1030                        && eon == null) {
1031
1032                        return BigInteger.valueOf((long) year);
1033                }
1034
1035        // neither are defined
1036        // or only eon is defined which is not valid without a year
1037                return null;
1038    }
1039
1040    /**
1041     * <p>Return number of month or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1042     *
1043     * <p>Value constraints for this value are summarized in
1044     * <a href="#datetimefield-month">month field of date/time field mapping table</a>.</p>
1045     *
1046     * @return year  of this <code>XMLGregorianCalendar</code>.
1047     *
1048     */
1049    public int getMonth() {
1050        return month;
1051    }
1052
1053    /**
1054     * Return day in month or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1055     *
1056     * <p>Value constraints for this value are summarized in
1057     * <a href="#datetimefield-day">day field of date/time field mapping table</a>.</p>
1058     *
1059     * @see #setDay(int)
1060     */
1061    public int getDay() {
1062        return day;
1063    }
1064
1065    /**
1066     * Return timezone offset in minutes or
1067     * {@link DatatypeConstants#FIELD_UNDEFINED} if this optional field is not defined.
1068     *
1069     * <p>Value constraints for this value are summarized in
1070     * <a href="#datetimefield-timezone">timezone field of date/time field mapping table</a>.</p>
1071     *
1072     * @see #setTimezone(int)
1073     */
1074    public int getTimezone() {
1075        return timezone;
1076    }
1077
1078    /**
1079     * Return hours or {@link DatatypeConstants#FIELD_UNDEFINED}.
1080     * Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1081     *
1082     * <p>Value constraints for this value are summarized in
1083     * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.</p>
1084     * @see #setTime(int, int, int)
1085     */
1086    public int getHour() {
1087        return hour;
1088    }
1089
1090    /**
1091     * Return minutes or {@link DatatypeConstants#FIELD_UNDEFINED}.<\p>
1092     * Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1093     *
1094     * <p>Value constraints for this value are summarized in
1095     * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.</p>
1096     * @see #setTime(int, int, int)
1097     */
1098    public int getMinute() {
1099        return minute;
1100    }
1101
1102    /**
1103     * <p>Return seconds or {@link DatatypeConstants#FIELD_UNDEFINED}.<\p>
1104     *
1105     * <p>Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1106     * When this field is not defined, the optional xs:dateTime
1107     * fractional seconds field, represented by
1108     * {@link #getFractionalSecond()} and {@link #getMillisecond()},
1109     * must not be defined.</p>
1110     *
1111     * <p>Value constraints for this value are summarized in
1112     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1113     *
1114     * @return Second  of this <code>XMLGregorianCalendar</code>.
1115     *
1116     * @see #getFractionalSecond()
1117     * @see #getMillisecond()
1118     * @see #setTime(int, int, int)
1119     */
1120    public int getSecond() {
1121           return second;
1122    }
1123
1124    /**
1125     * @return result of adding second and fractional second field
1126     */
1127    private BigDecimal getSeconds() {
1128        if (second == DatatypeConstants.FIELD_UNDEFINED) {
1129            return DECIMAL_ZERO;
1130        }
1131        BigDecimal result = BigDecimal.valueOf((long) second);
1132        if (fractionalSecond != null) {
1133            return result.add(fractionalSecond);
1134        } else {
1135            return result;
1136        }
1137    }
1138
1139
1140    /**
1141     * <p>Return millisecond precision of {@link #getFractionalSecond()}.<\p>
1142     *
1143     * <p>This method represents a convenience accessor to infinite
1144     * precision fractional second value returned by
1145     * {@link #getFractionalSecond()}. The returned value is the rounded
1146     * down to milliseconds value of
1147     * {@link #getFractionalSecond()}. When {@link #getFractionalSecond()}
1148     * returns <code>null</code>, this method must return
1149     * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1150     *
1151     * <p>Value constraints for this value are summarized in
1152     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1153     *
1154     * @return Millisecond  of this <code>XMLGregorianCalendar</code>.
1155     *
1156     * @see #getFractionalSecond()
1157     * @see #setTime(int, int, int)
1158     */
1159    public int getMillisecond() {
1160        if (fractionalSecond == null) {
1161            return DatatypeConstants.FIELD_UNDEFINED;
1162        } else {
1163            // TODO: Non-optimal solution for now.
1164            // Efficient implementation would only store as BigDecimal
1165            // when needed and millisecond otherwise.
1166            return fractionalSecond.movePointRight(3).intValue();
1167        }
1168    }
1169
1170    /**
1171     * <p>Return fractional seconds.</p>
1172     *
1173     * <p><code>null</code> is returned when this optional field is not defined.</p>
1174     *
1175     * <p>Value constraints are detailed in
1176     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1177     *
1178     * <p>This optional field can only have a defined value when the
1179     * xs:dateTime second field, represented by ({@link #getSecond()},
1180     * does not return {@link DatatypeConstants#FIELD_UNDEFINED}).</p>
1181     *
1182     * @return fractional seconds  of this <code>XMLGregorianCalendar</code>.
1183     *
1184     * @see #getSecond()
1185     * @see #setTime(int, int, int, BigDecimal)
1186     */
1187    public BigDecimal getFractionalSecond() {
1188           return fractionalSecond;
1189    }
1190
1191    // setters
1192
1193    /**
1194     * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
1195     *
1196     * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
1197     *
1198     * @param year value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1199     *
1200     * @throws IllegalArgumentException if <code>year</code> parameter is
1201     * outside value constraints for the field as specified in
1202     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1203     */
1204    public void setYear(BigInteger year) {
1205        if (year == null) {
1206            this.eon = null;
1207            this.year = DatatypeConstants.FIELD_UNDEFINED;
1208        } else {
1209            BigInteger temp = year.remainder(BILLION_B);
1210            this.year = temp.intValue();
1211            setEon(year.subtract(temp));
1212        }
1213    }
1214
1215    /**
1216     * <p>Set year of XSD <code>dateTime</code> year field.</p>
1217     *
1218     * <p>Unset this field by invoking the setter with a parameter value of
1219     * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1220     *
1221     * <p>Note: if the absolute value of the <code>year</code> parameter
1222     * is less than 10^9, the eon component of the XSD year field is set to
1223     * <code>null</code> by this method.</p>
1224     *
1225     * @param year value constraints are summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1226     *   If year is {@link DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
1227     */
1228    public void setYear(int year) {
1229        if (year == DatatypeConstants.FIELD_UNDEFINED) {
1230            this.year = DatatypeConstants.FIELD_UNDEFINED;
1231            this.eon = null;
1232        }
1233        else if (Math.abs(year) < BILLION_I) {
1234            this.year = year;
1235            this.eon = null;
1236        } else {
1237            BigInteger theYear = BigInteger.valueOf((long) year);
1238            BigInteger remainder = theYear.remainder(BILLION_B);
1239            this.year = remainder.intValue();
1240            setEon(theYear.subtract(remainder));
1241        }
1242    }
1243
1244    /**
1245     * <p>Set high order part of XSD <code>dateTime</code> year field.</p>
1246     *
1247     * <p>Unset this field by invoking the setter with a parameter value of
1248     * <code>null</code>.</p>
1249     *
1250     * @param eon value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1251     */
1252    private void setEon(BigInteger eon) {
1253        if (eon != null && eon.compareTo(BigInteger.ZERO) == 0) {
1254            // Treat ZERO as field being undefined.
1255            this.eon = null;
1256        } else {
1257            this.eon = eon;
1258        }
1259    }
1260
1261    /**
1262     * <p>Set month.</p>
1263     *
1264     * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1265     *
1266     * @param month value constraints summarized in <a href="#datetimefield-month">month field of date/time field mapping table</a>.
1267     *
1268     * @throws IllegalArgumentException if <code>month</code> parameter is
1269     * outside value constraints for the field as specified in
1270     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1271     */
1272    public void setMonth(int month) {
1273        if(month<DatatypeConstants.JANUARY || DatatypeConstants.DECEMBER<month)
1274            if(month!=DatatypeConstants.FIELD_UNDEFINED)
1275                invalidFieldValue(MONTH, month);
1276        this.month = month;
1277    }
1278
1279    /**
1280     * <p>Set days in month.</p>
1281     *
1282     * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1283     *
1284     * @param day value constraints summarized in <a href="#datetimefield-day">day field of date/time field mapping table</a>.
1285     *
1286     * @throws IllegalArgumentException if <code>day</code> parameter is
1287     * outside value constraints for the field as specified in
1288     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1289     */
1290    public void setDay(int day) {
1291        if(day<1 || 31<day)
1292            if(day!=DatatypeConstants.FIELD_UNDEFINED)
1293                invalidFieldValue(DAY,day);
1294        this.day = day;
1295    }
1296
1297    /**
1298     * <p>Set the number of minutes in the timezone offset.</p>
1299     *
1300     * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1301     *
1302     * @param offset value constraints summarized in <a href="#datetimefield-timezone">
1303     *   timezone field of date/time field mapping table</a>.
1304     *
1305     * @throws IllegalArgumentException if <code>offset</code> parameter is
1306     * outside value constraints for the field as specified in
1307     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1308     */
1309    public void setTimezone(int offset) {
1310            if(offset<-14*60 || 14*60<offset)
1311            if(offset!=DatatypeConstants.FIELD_UNDEFINED)
1312                invalidFieldValue(TIMEZONE,offset);
1313        this.timezone = offset;
1314    }
1315
1316    /**
1317     * <p>Set time as one unit.</p>
1318     *
1319     * @param hour value constraints are summarized in
1320     * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1321     * @param minute value constraints are summarized in
1322     * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1323     * @param second value constraints are summarized in
1324     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1325     *
1326     * @see #setTime(int, int, int, BigDecimal)
1327     *
1328     * @throws IllegalArgumentException if any parameter is
1329     * outside value constraints for the field as specified in
1330     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1331     */
1332    public void setTime(int hour, int minute, int second) {
1333        setTime(hour, minute, second, null);
1334    }
1335
1336    private void invalidFieldValue(int field, int value) {
1337        throw new IllegalArgumentException(
1338            DatatypeMessageFormatter.formatMessage(null, "InvalidFieldValue",
1339                new Object[]{ new Integer(value), FIELD_NAME[field]})
1340        );
1341    }
1342
1343    private void testHour() {
1344
1345        // http://www.w3.org/2001/05/xmlschema-errata#e2-45
1346        if (getHour() == 24) {
1347            if (getMinute() != 0
1348                    || getSecond() != 0) {
1349                invalidFieldValue(HOUR, getHour());
1350            }
1351            // while 0-24 is acceptable in the lexical space, 24 is not valid in value space
1352            // W3C XML Schema Part 2, Section 3.2.7.1
1353            setHour(0, false);
1354            add(new DurationImpl(true, 0, 0, 1, 0, 0, 0));
1355        }
1356    }
1357
1358    public void setHour(int hour) {
1359
1360        setHour(hour, true);
1361    }
1362
1363    private void setHour(int hour, boolean validate) {
1364
1365        if (hour < 0 || hour > 24) {
1366            if (hour != DatatypeConstants.FIELD_UNDEFINED) {
1367                invalidFieldValue(HOUR, hour);
1368            }
1369        }
1370
1371        this.hour = hour;
1372
1373        if (validate) {
1374            testHour();
1375        }
1376    }
1377
1378    public void setMinute(int minute) {
1379        if(minute<0 || 59<minute)
1380            if(minute!=DatatypeConstants.FIELD_UNDEFINED)
1381                invalidFieldValue(MINUTE, minute);
1382        this.minute = minute;
1383    }
1384
1385    public void setSecond(int second) {
1386        if(second<0 || 60<second)   // leap second allows for 60
1387            if(second!=DatatypeConstants.FIELD_UNDEFINED)
1388                invalidFieldValue(SECOND, second);
1389        this.second  = second;
1390    }
1391
1392    /**
1393     * <p>Set time as one unit, including the optional infinite precison
1394     * fractional seconds.</p>
1395     *
1396     * @param hour value constraints are summarized in
1397     * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1398     * @param minute value constraints are summarized in
1399     * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1400     * @param second value constraints are summarized in
1401     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1402     * @param fractional value of <code>null</code> indicates this optional
1403     *                   field is not set.
1404     *
1405     * @throws IllegalArgumentException if any parameter is
1406     * outside value constraints for the field as specified in
1407     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1408     */
1409    public void setTime(
1410            int hour,
1411            int minute,
1412            int second,
1413            BigDecimal fractional) {
1414
1415        setHour(hour, false);
1416
1417        setMinute(minute);
1418        if (second != 60) {
1419            setSecond(second);
1420        } else if ((hour == 23 && minute == 59) || (hour == 0 && minute == 0)) {
1421            setSecond(second);
1422        } else {
1423            invalidFieldValue(SECOND, second);
1424        }
1425
1426        setFractionalSecond(fractional);
1427
1428        // must test hour after setting seconds
1429        testHour();
1430    }
1431
1432
1433    /**
1434     * <p>Set time as one unit, including optional milliseconds.</p>
1435     *
1436     * @param hour value constraints are summarized in
1437     * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1438     * @param minute value constraints are summarized in
1439     * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1440     * @param second value constraints are summarized in
1441     * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1442     * @param millisecond value of {@link DatatypeConstants#FIELD_UNDEFINED} indicates this
1443     *                    optional field is not set.
1444     *
1445     * @throws IllegalArgumentException if any parameter is
1446     * outside value constraints for the field as specified in
1447     * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1448     */
1449    public void setTime(int hour, int minute, int second, int millisecond) {
1450
1451        setHour(hour, false);
1452
1453        setMinute(minute);
1454        if (second != 60) {
1455            setSecond(second);
1456        } else if ((hour == 23 && minute == 59) || (hour == 0 && minute == 0)) {
1457            setSecond(second);
1458        } else {
1459            invalidFieldValue(SECOND, second);
1460        }
1461        setMillisecond(millisecond);
1462
1463        // must test hour after setting seconds
1464        testHour();
1465    }
1466
1467    // comparisons
1468    /**
1469     * <p>Compare two instances of W3C XML Schema 1.0 date/time datatypes
1470     * according to partial order relation defined in
1471     * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.3,
1472     * <i>Order relation on dateTime</i></a>.</p>
1473     *
1474     * <p><code>xsd:dateTime</code> datatype field mapping to accessors of
1475     * this class are defined in
1476     * <a href="#datetimefieldmapping">date/time field mapping table</a>.</p>
1477     *
1478     * @param rhs instance of <code>XMLGregorianCalendar</code> to compare
1479     *
1480     * @return the relationship between <code>lhs</code> and <code>rhs</code> as
1481     *   {@link DatatypeConstants#LESSER},
1482     *   {@link DatatypeConstants#EQUAL},
1483     *   {@link DatatypeConstants#GREATER} or
1484     *   {@link DatatypeConstants#INDETERMINATE}.
1485     *
1486     * @throws NullPointerException if <code>lhs</code> or <code>rhs</code>
1487     * parameters are null.
1488     */
1489    public int compare(XMLGregorianCalendar rhs) {
1490
1491        XMLGregorianCalendar lhs = this;
1492
1493        int result = DatatypeConstants.INDETERMINATE;
1494        XMLGregorianCalendarImpl P = (XMLGregorianCalendarImpl) lhs;
1495        XMLGregorianCalendarImpl Q = (XMLGregorianCalendarImpl) rhs;
1496
1497        if (P.getTimezone() == Q.getTimezone()) {
1498            // Optimization:
1499            // both instances are in same timezone or
1500            // both are FIELD_UNDEFINED.
1501            // Avoid costly normalization of timezone to 'Z' time.
1502            return internalCompare(P, Q);
1503
1504        } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED &&
1505                Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1506
1507            // Both instances have different timezones.
1508            // Normalize to UTC time and compare.
1509            P = (XMLGregorianCalendarImpl) P.normalize();
1510            Q = (XMLGregorianCalendarImpl) Q.normalize();
1511            return internalCompare(P, Q);
1512        } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1513
1514            if (P.getTimezone() != 0) {
1515                P = (XMLGregorianCalendarImpl) P.normalize();
1516            }
1517
1518            // C. step 1
1519            XMLGregorianCalendar MinQ = Q.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1520            result = internalCompare(P, MinQ);
1521            if (result == DatatypeConstants.LESSER) {
1522                return result;
1523            }
1524
1525            // C. step 2
1526            XMLGregorianCalendar MaxQ = Q.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1527            result = internalCompare(P, MaxQ);
1528            if (result == DatatypeConstants.GREATER) {
1529                return result;
1530            } else {
1531                // C. step 3
1532                return DatatypeConstants.INDETERMINATE;
1533            }
1534        } else { // Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
1535            // P has no timezone and Q does.
1536            if (Q.getTimezone() != 0) {
1537                Q = (XMLGregorianCalendarImpl) Q.normalizeToTimezone(Q.getTimezone());
1538            }
1539
1540            // D. step 1
1541            XMLGregorianCalendar MaxP = P.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1542            result = internalCompare(MaxP, Q);
1543            if (result == DatatypeConstants.LESSER) {
1544                return result;
1545            }
1546
1547            // D. step 2
1548            XMLGregorianCalendar MinP = P.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1549            result = internalCompare(MinP, Q);
1550            if (result == DatatypeConstants.GREATER) {
1551                return result;
1552            } else {
1553                // D. step 3
1554                return DatatypeConstants.INDETERMINATE;
1555            }
1556        }
1557    }
1558
1559    /**
1560     * <p>Normalize this instance to UTC.</p>
1561     *
1562     * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1563     * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1564     */
1565    public XMLGregorianCalendar normalize() {
1566
1567        XMLGregorianCalendar normalized = normalizeToTimezone(timezone);
1568
1569        // if timezone was undefined, leave it undefined
1570        if (getTimezone() == DatatypeConstants.FIELD_UNDEFINED) {
1571            normalized.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
1572        }
1573
1574        // if milliseconds was undefined, leave it undefined
1575        if (getMillisecond() == DatatypeConstants.FIELD_UNDEFINED) {
1576            normalized.setMillisecond(DatatypeConstants.FIELD_UNDEFINED);
1577        }
1578
1579        return normalized;
1580    }
1581
1582        /**
1583         * <p>Normalize this instance to UTC.</p>
1584         *
1585         * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1586         * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1587         */
1588    private XMLGregorianCalendar normalizeToTimezone(int timezone) {
1589
1590        int minutes = timezone;
1591        XMLGregorianCalendar result = (XMLGregorianCalendar) this.clone();
1592
1593        // normalizing to UTC time negates the timezone offset before
1594        // addition.
1595        minutes = -minutes;
1596        Duration d = new DurationImpl(minutes >= 0, // isPositive
1597                0, //years
1598                0, //months
1599                0, //days
1600                0, //hours
1601                minutes < 0 ? -minutes : minutes, // absolute
1602                0  //seconds
1603        );
1604        result.add(d);
1605
1606        // set to zulu UTC time.
1607        result.setTimezone(0);
1608        return result;
1609    }
1610
1611    /**
1612     *
1613     *  <p>Implements Step B from http://www.w3.org/TR/xmlschema-2/#dateTime-order </p>
1614     * @param P calendar instance with normalized timezone offset or
1615     *          having same timezone as Q
1616     * @param Q calendar instance with normalized timezone offset or
1617     *          having same timezone as P
1618     *
1619     * @return result of comparing P and Q, value of
1620     *   {@link DatatypeConstants#EQUAL},
1621     *   {@link DatatypeConstants#LESSER},
1622     *   {@link DatatypeConstants#GREATER} or
1623     *   {@link DatatypeConstants#INDETERMINATE}.
1624     */
1625    private static int internalCompare(XMLGregorianCalendar P,
1626                                       XMLGregorianCalendar Q) {
1627
1628        int result;
1629
1630        // compare Year.
1631        if (P.getEon() == Q.getEon()) {
1632
1633            // Eon field is only equal when null.
1634            // optimized case for comparing year not requiring eon field.
1635            result = compareField(P.getYear(), Q.getYear());
1636            if (result != DatatypeConstants.EQUAL) {
1637                return result;
1638            }
1639        } else {
1640            result = compareField(P.getEonAndYear(), Q.getEonAndYear());
1641            if (result != DatatypeConstants.EQUAL) {
1642                return result;
1643            }
1644        }
1645
1646        result = compareField(P.getMonth(), Q.getMonth());
1647        if (result != DatatypeConstants.EQUAL) {
1648            return result;
1649        }
1650
1651        result = compareField(P.getDay(), Q.getDay());
1652        if (result != DatatypeConstants.EQUAL) {
1653            return result;
1654        }
1655
1656        result = compareField(P.getHour(), Q.getHour());
1657        if (result != DatatypeConstants.EQUAL) {
1658            return result;
1659        }
1660
1661        result = compareField(P.getMinute(), Q.getMinute());
1662        if (result != DatatypeConstants.EQUAL) {
1663            return result;
1664        }
1665        result = compareField(P.getSecond(), Q.getSecond());
1666        if (result != DatatypeConstants.EQUAL) {
1667            return result;
1668        }
1669
1670        result = compareField(P.getFractionalSecond(), Q.getFractionalSecond());
1671        return result;
1672    }
1673
1674    /**
1675     * <p>Implement Step B from
1676     * http://www.w3.org/TR/xmlschema-2/#dateTime-order.</p>
1677     */
1678    private static int compareField(int Pfield, int Qfield) {
1679        if (Pfield == Qfield) {
1680
1681            //fields are either equal in value or both undefined.
1682            // Step B. 1.1 AND optimized result of performing 1.1-1.4.
1683            return DatatypeConstants.EQUAL;
1684        } else {
1685            if (Pfield == DatatypeConstants.FIELD_UNDEFINED || Qfield == DatatypeConstants.FIELD_UNDEFINED) {
1686                // Step B. 1.2
1687                return DatatypeConstants.INDETERMINATE;
1688            } else {
1689                // Step B. 1.3-4.
1690                return (Pfield < Qfield ? DatatypeConstants.LESSER : DatatypeConstants.GREATER);
1691            }
1692        }
1693    }
1694
1695    private static int compareField(BigInteger Pfield, BigInteger Qfield) {
1696        if (Pfield == null) {
1697            return (Qfield == null ? DatatypeConstants.EQUAL : DatatypeConstants.INDETERMINATE);
1698        }
1699        if (Qfield == null) {
1700            return DatatypeConstants.INDETERMINATE;
1701        }
1702        return Pfield.compareTo(Qfield);
1703    }
1704
1705    private static int compareField(BigDecimal Pfield, BigDecimal Qfield) {
1706        // optimization. especially when both arguments are null.
1707        if (Pfield == Qfield) {
1708            return DatatypeConstants.EQUAL;
1709        }
1710
1711        if (Pfield == null) {
1712            Pfield = DECIMAL_ZERO;
1713        }
1714
1715        if (Qfield == null) {
1716            Qfield = DECIMAL_ZERO;
1717        }
1718
1719        return Pfield.compareTo(Qfield);
1720    }
1721
1722    /**
1723     * <p>Indicates whether parameter <code>obj</code> is "equal to" this one.</p>
1724     *
1725     * @param obj to compare.
1726     *
1727     * @return <code>true</code> when <code>compare(this,(XMLGregorianCalendar)obj) == EQUAL.</code>.
1728     */
1729    public boolean equals(Object obj) {
1730
1731        if (obj == null || !(obj instanceof XMLGregorianCalendar)) {
1732            return false;
1733        }
1734        if (obj == this) {
1735            return true;
1736        }
1737        return compare((XMLGregorianCalendar) obj) == DatatypeConstants.EQUAL;
1738    }
1739
1740    /**
1741     * <p>Returns a hash code consistent with the definition of the equals method.</p>
1742     *
1743     * @return hash code of this object.
1744     */
1745    public int hashCode() {
1746
1747        // Following two dates compare to EQUALS since in different timezones.
1748        // 2000-01-15T12:00:00-05:00 == 2000-01-15T13:00:00-04:00
1749        //
1750        // Must ensure both instances generate same hashcode by normalizing
1751        // this to UTC timezone.
1752        int timezone = getTimezone();
1753        if (timezone == DatatypeConstants.FIELD_UNDEFINED) {
1754            timezone = 0;
1755        }
1756        XMLGregorianCalendar gc = this;
1757        if (timezone != 0) {
1758            gc = this.normalizeToTimezone(getTimezone());
1759        }
1760        return gc.getYear() + gc.getMonth() + gc.getDay() +
1761                gc.getHour() + gc.getMinute() + gc.getSecond();
1762    }
1763
1764
1765    /**
1766     * <p>Constructs a new XMLGregorianCalendar object by
1767     * parsing its lexical string representation as defined in
1768     * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1769     * <i>Lexical Representation</i>.</a></p>
1770     *
1771     * <p>The string representation may not have any leading and trailing whitespaces.</p>
1772     *
1773     * <p>The parsing is done field by field so that
1774     * the following holds for any lexically correct string x:</p>
1775     * <pre>
1776     * new XMLGregorianCalendar(x).toXMLFormat().equals(x)
1777     * </pre>
1778     * Except for the noted lexical/canonical representation mismatches
1779     * listed in <a href="http://www.w3.org/2001/05/xmlschema-errata#e2-45">
1780     * XML Schema 1.0 errata, Section 3.2.7.2</a>.
1781     *
1782     * <p>Returns a non-null valid XMLGregorianCalendar object that holds the value
1783     * indicated by the lexicalRepresentation parameter.</p>
1784     *
1785     * @param lexicalRepresentation Lexical representation of one the 8 XML Schema calendar datatypes.
1786     *
1787     * @return <code>XMLGregorianCalendar</code> created from parsing <code>lexicalRepresentation</code> parameter.
1788     *
1789     * @throws IllegalArgumentException
1790     *      If the given string does not conform to the aforementioned
1791     *      specification.
1792     * @throws NullPointerException
1793     *      If the given string is null.
1794     */
1795    public static XMLGregorianCalendar parse(String lexicalRepresentation) {
1796
1797                return new XMLGregorianCalendarImpl(lexicalRepresentation);
1798    }
1799
1800    /**
1801     * <p>Return the lexical representation of <code>this</code> instance.
1802     * The format is specified in
1803     * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1804     * <i>Lexical Representation</i>".</a></p>
1805     *
1806     * <p>Specific target lexical representation format is determined by
1807     * {@link #getXMLSchemaType()}.</p>
1808     *
1809     * @return XML, as <code>String</code>, representation of this <code>XMLGregorianCalendar</code>
1810     *
1811     * @throws java.lang.IllegalStateException if the combination of set fields
1812     *    does not match one of the eight defined XML Schema builtin date/time datatypes.
1813     */
1814    public String toXMLFormat() {
1815
1816        QName typekind = getXMLSchemaType();
1817
1818        String formatString = null;
1819        // Fix 4971612: invalid SCCS macro substitution in data string
1820        //   no %{alpha}% to avoid SCCS macro substitution
1821        if (typekind == DatatypeConstants.DATETIME) {
1822            formatString = "%Y-%M-%DT%h:%m:%s" + "%z";
1823        } else if (typekind == DatatypeConstants.DATE) {
1824            formatString = "%Y-%M-%D" + "%z";
1825        } else if (typekind == DatatypeConstants.TIME) {
1826            formatString = "%h:%m:%s" + "%z";
1827        } else if (typekind == DatatypeConstants.GMONTH) {
1828            formatString = "--%M" + "%z";
1829        } else if (typekind == DatatypeConstants.GDAY) {
1830            formatString = "---%D" + "%z";
1831        } else if (typekind == DatatypeConstants.GYEAR) {
1832            formatString = "%Y" + "%z";
1833        } else if (typekind == DatatypeConstants.GYEARMONTH) {
1834            formatString = "%Y-%M" + "%z";
1835        } else if (typekind == DatatypeConstants.GMONTHDAY) {
1836            formatString = "--%M-%D" + "%z";
1837        }
1838        return format(formatString);
1839    }
1840
1841    /**
1842     * <p>Return the name of the XML Schema date/time type that this instance
1843     * maps to. Type is computed based on fields that are set.</p>
1844     *
1845     * <table border="2" rules="all" cellpadding="2">
1846     *   <thead>
1847     *     <tr>
1848     *       <th align="center" colspan="7">
1849     *         Required fields for XML Schema 1.0 Date/Time Datatypes.<br/>
1850     *         <i>(timezone is optional for all date/time datatypes)</i>
1851     *       </th>
1852     *     </tr>
1853     *   </thead>
1854     *   <tbody>
1855     *     <tr>
1856     *       <td>Datatype</td>
1857     *       <td>year</td>
1858     *       <td>month</td>
1859     *       <td>day</td>
1860     *       <td>hour</td>
1861     *       <td>minute</td>
1862     *       <td>second</td>
1863     *     </tr>
1864     *     <tr>
1865     *       <td>{@link DatatypeConstants#DATETIME}</td>
1866     *       <td>X</td>
1867     *       <td>X</td>
1868     *       <td>X</td>
1869     *       <td>X</td>
1870     *       <td>X</td>
1871     *       <td>X</td>
1872     *     </tr>
1873     *     <tr>
1874     *       <td>{@link DatatypeConstants#DATE}</td>
1875     *       <td>X</td>
1876     *       <td>X</td>
1877     *       <td>X</td>
1878     *       <td></td>
1879     *       <td></td>
1880     *       <td></td>
1881     *     </tr>
1882     *     <tr>
1883     *       <td>{@link DatatypeConstants#TIME}</td>
1884     *       <td></td>
1885     *       <td></td>
1886     *       <td></td>
1887     *       <td>X</td>
1888     *       <td>X</td>
1889     *       <td>X</td>
1890     *     </tr>
1891     *     <tr>
1892     *       <td>{@link DatatypeConstants#GYEARMONTH}</td>
1893     *       <td>X</td>
1894     *       <td>X</td>
1895     *       <td></td>
1896     *       <td></td>
1897     *       <td></td>
1898     *       <td></td>
1899     *     </tr>
1900     *     <tr>
1901     *       <td>{@link DatatypeConstants#GMONTHDAY}</td>
1902     *       <td></td>
1903     *       <td>X</td>
1904     *       <td>X</td>
1905     *       <td></td>
1906     *       <td></td>
1907     *       <td></td>
1908     *     </tr>
1909     *     <tr>
1910     *       <td>{@link DatatypeConstants#GYEAR}</td>
1911     *       <td>X</td>
1912     *       <td></td>
1913     *       <td></td>
1914     *       <td></td>
1915     *       <td></td>
1916     *       <td></td>
1917     *     </tr>
1918     *     <tr>
1919     *       <td>{@link DatatypeConstants#GMONTH}</td>
1920     *       <td></td>
1921     *       <td>X</td>
1922     *       <td></td>
1923     *       <td></td>
1924     *       <td></td>
1925     *       <td></td>
1926     *     </tr>
1927     *     <tr>
1928     *       <td>{@link DatatypeConstants#GDAY}</td>
1929     *       <td></td>
1930     *       <td></td>
1931     *       <td>X</td>
1932     *       <td></td>
1933     *       <td></td>
1934     *       <td></td>
1935     *     </tr>
1936     *   </tbody>
1937     * </table>
1938     *
1939     * @throws java.lang.IllegalStateException if the combination of set fields
1940     *    does not match one of the eight defined XML Schema builtin
1941     *    date/time datatypes.
1942     * @return One of the following class constants:
1943     *   {@link DatatypeConstants#DATETIME},
1944     *   {@link DatatypeConstants#TIME},
1945     *   {@link DatatypeConstants#DATE},
1946     *   {@link DatatypeConstants#GYEARMONTH},
1947     *   {@link DatatypeConstants#GMONTHDAY},
1948     *   {@link DatatypeConstants#GYEAR},
1949     *   {@link DatatypeConstants#GMONTH} or
1950     *   {@link DatatypeConstants#GDAY}.
1951     */
1952    public QName getXMLSchemaType() {
1953
1954        int mask =
1955            (year != DatatypeConstants.FIELD_UNDEFINED ?   0x20 : 0 )|
1956            (month != DatatypeConstants.FIELD_UNDEFINED ?  0x10 : 0 )|
1957            (day != DatatypeConstants.FIELD_UNDEFINED ?    0x08 : 0 )|
1958            (hour != DatatypeConstants.FIELD_UNDEFINED ?   0x04 : 0 )|
1959            (minute != DatatypeConstants.FIELD_UNDEFINED ? 0x02 : 0 )|
1960            (second != DatatypeConstants.FIELD_UNDEFINED ? 0x01 : 0 );
1961
1962        switch(mask) {
1963        case 0x3F:
1964                return DatatypeConstants.DATETIME;
1965        case 0x38:
1966                return DatatypeConstants.DATE;
1967        case 0x07:
1968                return DatatypeConstants.TIME;
1969        case 0x30:
1970                return DatatypeConstants.GYEARMONTH;
1971        case 0x18:
1972                return DatatypeConstants.GMONTHDAY;
1973        case 0x20:
1974                return DatatypeConstants.GYEAR;
1975        case 0x10:
1976                return DatatypeConstants.GMONTH;
1977        case 0x08:
1978                return DatatypeConstants.GDAY;
1979        default:
1980            throw new IllegalStateException(
1981                this.getClass().getName()
1982                + "#getXMLSchemaType() :"
1983                + DatatypeMessageFormatter.formatMessage(null, "InvalidXGCFields", null)
1984            );
1985        }
1986    }
1987
1988
1989    /**
1990     * Validate instance by <code>getXMLSchemaType()</code> constraints.
1991     * @return true if data values are valid.
1992     */
1993    public boolean isValid() {
1994        // since setters do not allow for invalid values,
1995        // (except for exceptional case of year field of zero),
1996        // no need to check for anything except for constraints
1997        // between fields.
1998
1999        // check if days in month is valid. Can be dependent on leap year.
2000        if (month != DatatypeConstants.FIELD_UNDEFINED && day != DatatypeConstants.FIELD_UNDEFINED) {
2001            if (year != DatatypeConstants.FIELD_UNDEFINED) {
2002                if (eon == null) {
2003                    if (day > maximumDayInMonthFor(year, month)) {
2004                        return false;
2005                    }
2006                }
2007                else if (day > maximumDayInMonthFor(getEonAndYear(), month)) {
2008                    return false;
2009                }
2010            }
2011            // Use 2000 as a default since it's a leap year.
2012            else if (day > maximumDayInMonthFor(2000, month)) {
2013                return false;
2014            }
2015        }
2016
2017        // http://www.w3.org/2001/05/xmlschema-errata#e2-45
2018        if (hour == 24 && (minute != 0 || second != 0 ||
2019                (fractionalSecond != null && fractionalSecond.compareTo(DECIMAL_ZERO) != 0))) {
2020            return false;
2021        }
2022
2023        // XML Schema 1.0 specification defines year value of zero as
2024        // invalid. Allow this class to set year field to zero
2025        // since XML Schema 1.0 errata states that lexical zero will
2026        // be allowed in next version and treated as 1 B.C.E.
2027        if (eon == null && year == 0) {
2028            return false;
2029        }
2030        return true;
2031    }
2032
2033    /**
2034     * <p>Add <code>duration</code> to this instance.<\p>
2035     *
2036     * <p>The computation is specified in
2037     * <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
2038     * <i>Adding durations to dateTimes</i>></a>.
2039     * <a href="#datetimefieldsmapping">date/time field mapping table</a>
2040     * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
2041     * to this class' representation of those fields.</p>
2042     *
2043     * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
2044     *
2045     * @throws NullPointerException  when <code>duration</code> parameter is <code>null</code>.
2046     */
2047    public void add(Duration duration) {
2048
2049        /*
2050           * Extracted from
2051           * http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
2052           * to ensure implemented properly. See spec for definitions of methods
2053           * used in algorithm.
2054           *
2055           * Given a dateTime S and a duration D, specifies how to compute a
2056           * dateTime E where E is the end of the time period with start S and
2057           * duration D i.e. E = S + D.
2058           *
2059           * The following is the precise specification.
2060           * These steps must be followed in the same order.
2061           * If a field in D is not specified, it is treated as if it were zero.
2062           * If a field in S is not specified, it is treated in the calculation
2063           * as if it were the minimum allowed value in that field, however,
2064           * after the calculation is concluded, the corresponding field in
2065           * E is removed (set to unspecified).
2066           *
2067           * Months (may be modified additionally below)
2068               *  temp := S[month] + D[month]
2069               *  E[month] := modulo(temp, 1, 13)
2070               *  carry := fQuotient(temp, 1, 13)
2071           */
2072
2073        boolean fieldUndefined[] = {
2074                false,
2075                false,
2076                false,
2077                false,
2078                false,
2079                false
2080        };
2081
2082        int signum = duration.getSign();
2083
2084        int startMonth = getMonth();
2085        if (startMonth == DatatypeConstants.FIELD_UNDEFINED) {
2086            startMonth = DatatypeConstants.JANUARY;
2087            fieldUndefined[MONTH] = true;
2088        }
2089
2090        BigInteger dMonths = sanitize(duration.getField(DatatypeConstants.MONTHS), signum);
2091        BigInteger temp = BigInteger.valueOf((long) startMonth).add(dMonths);
2092        setMonth(temp.subtract(BigInteger.ONE).mod(TWELVE).intValue() + 1);
2093        BigInteger carry =
2094                new BigDecimal(temp.subtract(BigInteger.ONE)).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_FLOOR).toBigInteger();
2095
2096        /* Years (may be modified additionally below)
2097            *  E[year] := S[year] + D[year] + carry
2098            */
2099        BigInteger startYear = getEonAndYear();
2100        if (startYear == null) {
2101            fieldUndefined[YEAR] = true;
2102            startYear = BigInteger.ZERO;
2103        }
2104        BigInteger dYears = sanitize(duration.getField(DatatypeConstants.YEARS), signum);
2105        BigInteger endYear = startYear.add(dYears).add(carry);
2106        setYear(endYear);
2107
2108        /* Zone
2109               *  E[zone] := S[zone]
2110           *
2111           * no-op since adding to this, not to a new end point.
2112           */
2113
2114        /* Seconds
2115            *  temp := S[second] + D[second]
2116            *  E[second] := modulo(temp, 60)
2117            *  carry := fQuotient(temp, 60)
2118            */
2119        BigDecimal startSeconds;
2120        if (getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
2121            fieldUndefined[SECOND] = true;
2122            startSeconds = DECIMAL_ZERO;
2123        } else {
2124            // seconds + fractionalSeconds
2125            startSeconds = getSeconds();
2126        }
2127
2128        // Duration seconds is SECONDS + FRACTIONALSECONDS.
2129        BigDecimal dSeconds = DurationImpl.sanitize((BigDecimal) duration.getField(DatatypeConstants.SECONDS), signum);
2130        BigDecimal tempBD = startSeconds.add(dSeconds);
2131        BigDecimal fQuotient =
2132                new BigDecimal(new BigDecimal(tempBD.toBigInteger()).divide(DECIMAL_SIXTY, BigDecimal.ROUND_FLOOR).toBigInteger());
2133        BigDecimal endSeconds = tempBD.subtract(fQuotient.multiply(DECIMAL_SIXTY));
2134
2135        carry = fQuotient.toBigInteger();
2136        setSecond(endSeconds.intValue());
2137        BigDecimal tempFracSeconds = endSeconds.subtract(new BigDecimal(BigInteger.valueOf((long) getSecond())));
2138        if (tempFracSeconds.compareTo(DECIMAL_ZERO) < 0) {
2139            setFractionalSecond(DECIMAL_ONE.add(tempFracSeconds));
2140            if (getSecond() == 0) {
2141                setSecond(59);
2142                carry = carry.subtract(BigInteger.ONE);
2143            } else {
2144                setSecond(getSecond() - 1);
2145            }
2146        } else {
2147            setFractionalSecond(tempFracSeconds);
2148        }
2149
2150        /* Minutes
2151               *  temp := S[minute] + D[minute] + carry
2152               *  E[minute] := modulo(temp, 60)
2153               *  carry := fQuotient(temp, 60)
2154           */
2155        int startMinutes = getMinute();
2156        if (startMinutes == DatatypeConstants.FIELD_UNDEFINED) {
2157            fieldUndefined[MINUTE] = true;
2158            startMinutes = 0;
2159        }
2160        BigInteger dMinutes = sanitize(duration.getField(DatatypeConstants.MINUTES), signum);
2161
2162        temp = BigInteger.valueOf(startMinutes).add(dMinutes).add(carry);
2163        setMinute(temp.mod(SIXTY).intValue());
2164        carry = new BigDecimal(temp).divide(DECIMAL_SIXTY, BigDecimal.ROUND_FLOOR).toBigInteger();
2165
2166        /* Hours
2167               *  temp := S[hour] + D[hour] + carry
2168               *  E[hour] := modulo(temp, 24)
2169               *  carry := fQuotient(temp, 24)
2170           */
2171        int startHours = getHour();
2172        if (startHours == DatatypeConstants.FIELD_UNDEFINED) {
2173            fieldUndefined[HOUR] = true;
2174            startHours = 0;
2175        }
2176        BigInteger dHours = sanitize(duration.getField(DatatypeConstants.HOURS), signum);
2177
2178        temp = BigInteger.valueOf(startHours).add(dHours).add(carry);
2179        setHour(temp.mod(TWENTY_FOUR).intValue(), false);
2180        carry = new BigDecimal(temp).divide(new BigDecimal(TWENTY_FOUR), BigDecimal.ROUND_FLOOR).toBigInteger();
2181
2182        /* Days
2183           *  if S[day] > maximumDayInMonthFor(E[year], E[month])
2184           *       + tempDays := maximumDayInMonthFor(E[year], E[month])
2185           *  else if S[day] < 1
2186           *       + tempDays := 1
2187           *  else
2188           *       + tempDays := S[day]
2189           *  E[day] := tempDays + D[day] + carry
2190           *  START LOOP
2191           *       + IF E[day] < 1
2192           *             # E[day] := E[day] +
2193            *                 maximumDayInMonthFor(E[year], E[month] - 1)
2194           *             # carry := -1
2195           *       + ELSE IF E[day] > maximumDayInMonthFor(E[year], E[month])
2196           *             # E[day] :=
2197            *                    E[day] - maximumDayInMonthFor(E[year], E[month])
2198           *             # carry := 1
2199           *       + ELSE EXIT LOOP
2200           *       + temp := E[month] + carry
2201           *       + E[month] := modulo(temp, 1, 13)
2202           *       + E[year] := E[year] + fQuotient(temp, 1, 13)
2203           *       + GOTO START LOOP
2204           */
2205        BigInteger tempDays;
2206        int startDay = getDay();
2207        if (startDay == DatatypeConstants.FIELD_UNDEFINED) {
2208            fieldUndefined[DAY] = true;
2209            startDay = 1;
2210        }
2211        BigInteger dDays = sanitize(duration.getField(DatatypeConstants.DAYS), signum);
2212        int maxDayInMonth = maximumDayInMonthFor(getEonAndYear(), getMonth());
2213        if (startDay > maxDayInMonth) {
2214            tempDays = BigInteger.valueOf(maxDayInMonth);
2215        } else if (startDay < 1) {
2216            tempDays = BigInteger.ONE;
2217        } else {
2218            tempDays = BigInteger.valueOf(startDay);
2219        }
2220        BigInteger endDays = tempDays.add(dDays).add(carry);
2221        int monthCarry;
2222        int intTemp;
2223        while (true) {
2224            if (endDays.compareTo(BigInteger.ONE) < 0) {
2225                // calculate days in previous month, watch for month roll over
2226                BigInteger mdimf = null;
2227                if (month >= 2) {
2228                    mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth() - 1));
2229                } else {
2230                    // roll over to December of previous year
2231                    mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear().subtract(BigInteger.valueOf((long) 1)), 12));
2232                }
2233                endDays = endDays.add(mdimf);
2234                monthCarry = -1;
2235            } else if (endDays.compareTo(BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
2236                endDays = endDays.add(BigInteger.valueOf(-maximumDayInMonthFor(getEonAndYear(), getMonth())));
2237                monthCarry = 1;
2238            } else {
2239                break;
2240            }
2241
2242            intTemp = getMonth() + monthCarry;
2243            int endMonth = (intTemp - 1) % (13 - 1);
2244            int quotient;
2245            if (endMonth < 0) {
2246                endMonth = (13 - 1) + endMonth + 1;
2247                quotient = BigDecimal.valueOf(intTemp - 1).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_UP).intValue();
2248            } else {
2249                quotient = (intTemp - 1) / (13 - 1);
2250                endMonth += 1;
2251            }
2252            setMonth(endMonth);
2253            if (quotient != 0) {
2254                setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
2255            }
2256        }
2257        setDay(endDays.intValue());
2258
2259        // set fields that where undefined before this addition, back to undefined.
2260        for (int i = YEAR; i <= SECOND; i++) {
2261            if (fieldUndefined[i]) {
2262                switch (i) {
2263                case YEAR:
2264                    setYear(DatatypeConstants.FIELD_UNDEFINED);
2265                    break;
2266                case MONTH:
2267                    setMonth(DatatypeConstants.FIELD_UNDEFINED);
2268                    break;
2269                case DAY:
2270                    setDay(DatatypeConstants.FIELD_UNDEFINED);
2271                    break;
2272                case HOUR:
2273                    setHour(DatatypeConstants.FIELD_UNDEFINED, false);
2274                    break;
2275                case MINUTE:
2276                    setMinute(DatatypeConstants.FIELD_UNDEFINED);
2277                    break;
2278                case SECOND:
2279                    setSecond(DatatypeConstants.FIELD_UNDEFINED);
2280                    setFractionalSecond(null);
2281                    break;
2282                }
2283            }
2284        }
2285    }
2286
2287    private static final BigInteger FOUR = BigInteger.valueOf(4);
2288    private static final BigInteger HUNDRED = BigInteger.valueOf(100);
2289    private static final BigInteger FOUR_HUNDRED = BigInteger.valueOf(400);
2290    private static final BigInteger SIXTY = BigInteger.valueOf(60);
2291    private static final BigInteger TWENTY_FOUR = BigInteger.valueOf(24);
2292    private static final BigInteger TWELVE = BigInteger.valueOf(12);
2293    private static final BigDecimal DECIMAL_ZERO = BigDecimal.valueOf(0);
2294    private static final BigDecimal DECIMAL_ONE = BigDecimal.valueOf(1);
2295    private static final BigDecimal DECIMAL_SIXTY = BigDecimal.valueOf(60);
2296
2297
2298    private static class DaysInMonth {
2299        private static final int [] table = { 0,  // XML Schema months start at 1.
2300            31, 28, 31, 30, 31, 30,
2301            31, 31, 30, 31, 30, 31};
2302    }
2303
2304    private static int maximumDayInMonthFor(BigInteger year, int month) {
2305        if (month != DatatypeConstants.FEBRUARY) {
2306            return DaysInMonth.table[month];
2307        } else {
2308            if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
2309                    (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
2310                            year.mod(FOUR).equals(BigInteger.ZERO))) {
2311                // is a leap year.
2312                return 29;
2313            } else {
2314                return DaysInMonth.table[month];
2315            }
2316        }
2317    }
2318
2319    private static int maximumDayInMonthFor(int year, int month) {
2320        if (month != DatatypeConstants.FEBRUARY) {
2321            return DaysInMonth.table[month];
2322        } else {
2323            if (((year % 400) == 0) ||
2324                    (((year % 100) != 0) && ((year % 4) == 0))) {
2325                // is a leap year.
2326                return 29;
2327            } else {
2328                return DaysInMonth.table[DatatypeConstants.FEBRUARY];
2329            }
2330        }
2331    }
2332
2333    /**
2334     * <p>Convert <code>this</code> to <code>java.util.GregorianCalendar</code>.</p>
2335     *
2336     * <p>When <code>this</code> instance has an undefined field, this
2337     * conversion relies on the <code>java.util.GregorianCalendar</code> default
2338     * for its corresponding field. A notable difference between
2339     * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
2340     * is that Timezone value is optional for date/time datatypes and it is
2341     * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
2342     * for <code>java.util.TimeZone.getDefault()</code> on how the default
2343     * is determined. To explicitly specify the <code>TimeZone</code>
2344     * instance, see
2345     * {@link #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)}.</p>
2346     *
2347     * <table border="2" rules="all" cellpadding="2">
2348     *   <thead>
2349     *     <tr>
2350     *       <th align="center" colspan="2">
2351     *          Field by Field Conversion from this class to
2352     *          <code>java.util.GregorianCalendar</code>
2353     *       </th>
2354     *     </tr>
2355     *   </thead>
2356     *   <tbody>
2357     *     <tr>
2358     *        <th><code>java.util.GregorianCalendar</code> field</th>
2359     *        <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
2360     *     </tr>
2361     *     <tr>
2362     *       <th><code>ERA</code></th>
2363     *       <th>{@link #getEonAndYear()}<code>.signum() < 0 ? GregorianCalendar.BC : GregorianCalendar.AD</code></th>
2364     *     </tr>
2365     *     <tr>
2366     *       <th><code>YEAR</code></th>
2367     *       <th>{@link #getEonAndYear()}<code>.abs().intValue()</code><i>*</i></th>
2368     *     </tr>
2369     *     <tr>
2370     *       <th><code>MONTH</code></th>
2371     *       <th>{@link #getMonth()}<code> - 1</code></th>
2372     *     </tr>
2373     *     <tr>
2374     *       <th><code>DAY_OF_MONTH</code></th>
2375     *       <th>{@link #getDay()}</th>
2376     *     </tr>
2377     *     <tr>
2378     *       <th><code>AM_PM</code></th>
2379     *       <th>{@link #getHour()} < 12 : Calendar.AM : Calendar.PM</th>
2380     *     </tr>
2381     *     <tr>
2382     *       <th><code>HOUR_OF_DAY</code></th>
2383     *       <th>{@link #getHour()}</th>
2384     *     </tr>
2385     *     <tr>
2386     *       <th><code>MINUTE</code></th>
2387     *       <th>{@link #getMinute()}</th>
2388     *     </tr>
2389     *     <tr>
2390     *       <th><code>SECOND</code></th>
2391     *       <th>{@link #getSecond()}</th>
2392     *     </tr>
2393     *     <tr>
2394     *       <th><code>MILLISECOND</code></th>
2395     *       <th>get millisecond order from {@link #getFractionalSecond()}<i>*</i> </th>
2396     *     </tr>
2397     *     <tr>
2398     *       <th><code>GregorianCalendar.setTimeZone(TimeZone)</code></th>
2399     *       <th>{@link #getTimezone()} formatted into Custom timezone id</th>
2400     *     </tr>
2401     *   </tbody>
2402     * </table>
2403     * <i>*</i> designates possible loss of precision during the conversion due
2404     * to source datatype having higer precison than target datatype.
2405     *
2406     * <p>To ensure consistency in conversion implementations, the new
2407     * <code>GregorianCalendar</code> should be instantiated in following
2408     * manner.
2409     * <ul>
2410     *   <li>Using <code>timeZone</code> value as defined above, create a new
2411     * <code>java.util.GregorianCalendar(timeZone,Locale.getDefault())</code>.
2412     *   </li>
2413     *   <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2414     *   <li>Obtain a pure Gregorian Calendar by invoking
2415     *   <code>GregorianCalendar.setGregorianChange(
2416     *   new Date(Long.MIN_VALUE))</code>.</li>
2417     *   <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2418     *       MINUTE, SECOND and MILLISECOND are set using the method
2419     *       <code>Calendar.set(int,int)</code></li>
2420     * </ul>
2421     * </p>
2422     *
2423     * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, XMLGregorianCalendar)
2424     */
2425    public java.util.GregorianCalendar toGregorianCalendar() {
2426
2427        GregorianCalendar result = null;
2428        final int DEFAULT_TIMEZONE_OFFSET = DatatypeConstants.FIELD_UNDEFINED;
2429        TimeZone tz = getTimeZone(DEFAULT_TIMEZONE_OFFSET);
2430        /** Use the following instead for JDK7 only:
2431         * Locale locale = Locale.getDefault(Locale.Category.FORMAT);
2432         */
2433        Locale locale = getDefaultLocale();
2434
2435        result = new GregorianCalendar(tz, locale);
2436        result.clear();
2437        result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2438
2439        // if year( and eon) are undefined, leave default Calendar values
2440        if (year != DatatypeConstants.FIELD_UNDEFINED) {
2441            if (eon == null) {
2442                result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2443                result.set(Calendar.YEAR, Math.abs(year));
2444            }
2445            else {
2446                BigInteger eonAndYear = getEonAndYear();
2447                result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2448                result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2449            }
2450        }
2451
2452        // only set month if it is set
2453        if (month != DatatypeConstants.FIELD_UNDEFINED) {
2454            // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2455            result.set(Calendar.MONTH, month - 1);
2456        }
2457
2458        // only set day if it is set
2459        if (day != DatatypeConstants.FIELD_UNDEFINED) {
2460            result.set(Calendar.DAY_OF_MONTH, day);
2461        }
2462
2463        // only set hour if it is set
2464        if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2465            result.set(Calendar.HOUR_OF_DAY, hour);
2466        }
2467
2468        // only set minute if it is set
2469        if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2470            result.set(Calendar.MINUTE, minute);
2471        }
2472
2473        // only set second if it is set
2474        if (second != DatatypeConstants.FIELD_UNDEFINED) {
2475            result.set(Calendar.SECOND, second);
2476        }
2477
2478        // only set millisend if it is set
2479        if (fractionalSecond != null) {
2480            result.set(Calendar.MILLISECOND, getMillisecond());
2481        }
2482
2483        return result;
2484    }
2485
2486    /**
2487     *
2488     * @return default locale
2489     */
2490    private Locale getDefaultLocale() {
2491
2492        String lang = SecuritySupport.getSystemProperty("user.language.format");
2493        String country = SecuritySupport.getSystemProperty("user.country.format");
2494        String variant = SecuritySupport.getSystemProperty("user.variant.format");
2495        Locale locale = null;
2496        if (lang != null) {
2497            if (country != null) {
2498                if (variant != null) {
2499                    locale = new Locale(lang, country, variant);
2500                } else {
2501                    locale = new Locale(lang, country);
2502                }
2503            } else {
2504                locale = new Locale(lang);
2505            }
2506        }
2507        if (locale == null) {
2508            locale = Locale.getDefault();
2509        }
2510        return locale;
2511    }
2512
2513    /**
2514     * <p>Convert <code>this</code> along with provided parameters
2515     * to <code>java.util.GregorianCalendar</code> instance.</p>
2516     *
2517     * <p> Since XML Schema 1.0 date/time datetypes has no concept of
2518     * timezone ids or daylight savings timezone ids, this conversion operation
2519     * allows the user to explicitly specify one with
2520     * <code>timezone</code> parameter.</p>
2521     *
2522     * <p>To compute the return value's <code>TimeZone</code> field,
2523     * <ul>
2524     * <li>when parameter <code>timeZone</code> is non-null,
2525     * it is the timezone field.</li>
2526     * <li>else when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2527     * create a <code>java.util.TimeZone</code> with a custom timezone id
2528     * using the <code>this.getTimezone()</code>.</li>
2529     * <li>else when <code>defaults.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2530     * create a <code>java.util.TimeZone</code> with a custom timezone id
2531     * using <code>defaults.getTimezone()</code>.</li>
2532     * <li>else use the <code>GregorianCalendar</code> default timezone value
2533     * for the host is definedas specified by
2534     * <code>java.util.TimeZone.getDefault()</code>.</li></p>
2535     *
2536     * <p>To ensure consistency in conversion implementations, the new
2537     * <code>GregorianCalendar</code> should be instantiated in following
2538     * manner.
2539     * <ul>
2540     *   <li>Create a new <code>java.util.GregorianCalendar(TimeZone,
2541     *       Locale)</code> with TimeZone set as specified above and the
2542     *       <code>Locale</code> parameter.
2543     *   </li>
2544     *   <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2545     *   <li>Obtain a pure Gregorian Calendar by invoking
2546     *   <code>GregorianCalendar.setGregorianChange(
2547     *   new Date(Long.MIN_VALUE))</code>.</li>
2548     *   <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2549     *       MINUTE, SECOND and MILLISECOND are set using the method
2550     *       <code>Calendar.set(int,int)</code></li>
2551     * </ul>
2552     *
2553     * @param timezone provide Timezone. <code>null</code> is a legal value.
2554     * @param aLocale  provide explicit Locale. Use default GregorianCalendar locale if
2555     *                 value is <code>null</code>.
2556     * @param defaults provide default field values to use when corresponding
2557     *                 field for this instance is DatatypeConstants.FIELD_UNDEFINED or null.
2558     *                 If <code>defaults</code>is <code>null</code> or a field
2559     *                 within the specified <code>defaults</code> is undefined,
2560     *                 just use <code>java.util.GregorianCalendar</code> defaults.
2561     * @return a java.util.GregorianCalendar conversion of this instance.
2562     *
2563     * @see #LEAP_YEAR_DEFAULT
2564     */
2565    public GregorianCalendar toGregorianCalendar(TimeZone timezone,
2566                                                 Locale aLocale,
2567                                                 XMLGregorianCalendar defaults) {
2568        GregorianCalendar result = null;
2569        TimeZone tz = timezone;
2570        if (tz == null) {
2571            int defaultZoneoffset = DatatypeConstants.FIELD_UNDEFINED;
2572            if (defaults != null) {
2573                defaultZoneoffset = defaults.getTimezone();
2574            }
2575            tz = getTimeZone(defaultZoneoffset);
2576        }
2577        if (aLocale == null) {
2578            aLocale = Locale.getDefault();
2579        }
2580        result = new GregorianCalendar(tz, aLocale);
2581        result.clear();
2582        result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2583
2584        // if year( and eon) are undefined, leave default Calendar values
2585        if (year != DatatypeConstants.FIELD_UNDEFINED) {
2586            if (eon == null) {
2587                result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2588                result.set(Calendar.YEAR, Math.abs(year));
2589            }
2590            else {
2591                final BigInteger eonAndYear = getEonAndYear();
2592                result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2593                result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2594            }
2595        } else {
2596            // use default if set
2597            if (defaults != null) {
2598                final int defaultYear = defaults.getYear();
2599                if (defaultYear != DatatypeConstants.FIELD_UNDEFINED) {
2600                    if (defaults.getEon() == null) {
2601                        result.set(Calendar.ERA, defaultYear < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2602                        result.set(Calendar.YEAR, Math.abs(defaultYear));
2603                    }
2604                    else {
2605                        final BigInteger defaultEonAndYear = defaults.getEonAndYear();
2606                        result.set(Calendar.ERA, defaultEonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2607                        result.set(Calendar.YEAR, defaultEonAndYear.abs().intValue());
2608                    }
2609                }
2610            }
2611        }
2612
2613        // only set month if it is set
2614        if (month != DatatypeConstants.FIELD_UNDEFINED) {
2615            // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2616            result.set(Calendar.MONTH, month - 1);
2617        } else {
2618            // use default if set
2619            final int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
2620            if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
2621                // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2622                result.set(Calendar.MONTH, defaultMonth - 1);
2623            }
2624        }
2625
2626        // only set day if it is set
2627        if (day != DatatypeConstants.FIELD_UNDEFINED) {
2628            result.set(Calendar.DAY_OF_MONTH, day);
2629        } else {
2630            // use default if set
2631            final int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
2632            if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
2633                result.set(Calendar.DAY_OF_MONTH, defaultDay);
2634            }
2635        }
2636
2637        // only set hour if it is set
2638        if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2639            result.set(Calendar.HOUR_OF_DAY, hour);
2640        } else {
2641            // use default if set
2642            int defaultHour = (defaults != null) ? defaults.getHour() : DatatypeConstants.FIELD_UNDEFINED;
2643            if (defaultHour != DatatypeConstants.FIELD_UNDEFINED) {
2644                result.set(Calendar.HOUR_OF_DAY, defaultHour);
2645            }
2646        }
2647
2648        // only set minute if it is set
2649        if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2650            result.set(Calendar.MINUTE, minute);
2651        } else {
2652            // use default if set
2653            final int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
2654            if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
2655                result.set(Calendar.MINUTE, defaultMinute);
2656            }
2657        }
2658
2659        // only set second if it is set
2660        if (second != DatatypeConstants.FIELD_UNDEFINED) {
2661            result.set(Calendar.SECOND, second);
2662        } else {
2663            // use default if set
2664            final int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
2665            if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
2666                result.set(Calendar.SECOND, defaultSecond);
2667            }
2668        }
2669
2670        // only set millisend if it is set
2671        if (fractionalSecond != null) {
2672            result.set(Calendar.MILLISECOND, getMillisecond());
2673        } else {
2674            // use default if set
2675            final BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
2676            if (defaultFractionalSecond != null) {
2677                result.set(Calendar.MILLISECOND, defaults.getMillisecond());
2678            }
2679        }
2680
2681        return result;
2682    }
2683
2684    /**
2685     * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
2686     *
2687     * <p>If timezone field is defined for this instance,
2688     * returns TimeZone initialized with custom timezone id
2689     * of zoneoffset. If timezone field is undefined,
2690     * try the defaultZoneoffset that was passed in.
2691     * If defaultZoneoffset is DatatypeConstants.FIELD_UNDEFINED, return
2692     * default timezone for this host.
2693     * (Same default as java.util.GregorianCalendar).</p>
2694     *
2695     * @param defaultZoneoffset default zoneoffset if this zoneoffset is
2696     * {@link DatatypeConstants#FIELD_UNDEFINED}.
2697     *
2698     * @return TimeZone for this.
2699     */
2700    public TimeZone getTimeZone(int defaultZoneoffset) {
2701        TimeZone result = null;
2702        int zoneoffset = getTimezone();
2703
2704        if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2705            zoneoffset = defaultZoneoffset;
2706        }
2707        if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2708            result = TimeZone.getDefault();
2709        } else {
2710            // zoneoffset is in minutes. Convert to custom timezone id format.
2711            char sign = zoneoffset < 0 ? '-' : '+';
2712            if (sign == '-') {
2713                zoneoffset = -zoneoffset;
2714            }
2715            int hour = zoneoffset / 60;
2716            int minutes = zoneoffset - (hour * 60);
2717
2718            // Javadoc for java.util.TimeZone documents max length
2719            // for customTimezoneId is 8 when optional ':' is not used.
2720            // Format is
2721            // "GMT" ('-'|''+') (digit digit?) (digit digit)?
2722            //                   hour          minutes
2723            StringBuffer customTimezoneId = new StringBuffer(8);
2724            customTimezoneId.append("GMT");
2725            customTimezoneId.append(sign);
2726            customTimezoneId.append(hour);
2727            if (minutes != 0) {
2728                if (minutes < 10) {
2729                    customTimezoneId.append('0');
2730                }
2731                customTimezoneId.append(minutes);
2732            }
2733            result = TimeZone.getTimeZone(customTimezoneId.toString());
2734        }
2735        return result;
2736    }
2737
2738    /**
2739     * <p>Creates and returns a copy of this object.</p>
2740     *
2741     * @return copy of this <code>Object</code>
2742     */
2743   public Object clone() {
2744        // Both this.eon and this.fractionalSecond are instances
2745        // of immutable classes, so they do not need to be cloned.
2746       return new XMLGregorianCalendarImpl(getEonAndYear(),
2747                        this.month, this.day,
2748                        this.hour, this.minute, this.second,
2749                        this.fractionalSecond,
2750                        this.timezone);
2751    }
2752
2753    /**
2754     * <p>Unset all fields to undefined.</p>
2755     *
2756     * <p>Set all int fields to {@link DatatypeConstants#FIELD_UNDEFINED} and reference fields
2757     * to null.</p>
2758     */
2759    public void clear() {
2760        eon = null;
2761        year = DatatypeConstants.FIELD_UNDEFINED;
2762        month = DatatypeConstants.FIELD_UNDEFINED;
2763        day = DatatypeConstants.FIELD_UNDEFINED;
2764        timezone = DatatypeConstants.FIELD_UNDEFINED;  // in minutes
2765        hour = DatatypeConstants.FIELD_UNDEFINED;
2766        minute = DatatypeConstants.FIELD_UNDEFINED;
2767        second = DatatypeConstants.FIELD_UNDEFINED;
2768        fractionalSecond = null;
2769    }
2770
2771    public void setMillisecond(int millisecond) {
2772        if (millisecond == DatatypeConstants.FIELD_UNDEFINED) {
2773            fractionalSecond = null;
2774        } else {
2775            if(millisecond<0 || 999<millisecond)
2776                if(millisecond!=DatatypeConstants.FIELD_UNDEFINED)
2777                    invalidFieldValue(MILLISECOND, millisecond);
2778            fractionalSecond = BigDecimal.valueOf(millisecond, 3);
2779        }
2780    }
2781
2782    public void setFractionalSecond(BigDecimal fractional) {
2783        if (fractional != null) {
2784            if ((fractional.compareTo(DECIMAL_ZERO) < 0) ||
2785                    (fractional.compareTo(DECIMAL_ONE) > 0)) {
2786                throw new IllegalArgumentException(DatatypeMessageFormatter.formatMessage(null,
2787                        "InvalidFractional", new Object[]{fractional}));
2788            }
2789        }
2790        this.fractionalSecond = fractional;
2791    }
2792
2793    private final class Parser {
2794        private final String format;
2795        private final String value;
2796
2797        private final int flen;
2798        private final int vlen;
2799
2800        private int fidx;
2801        private int vidx;
2802
2803        private Parser(String format, String value) {
2804            this.format = format;
2805            this.value = value;
2806            this.flen = format.length();
2807            this.vlen = value.length();
2808        }
2809
2810        /**
2811         * <p>Parse a formated <code>String</code> into an <code>XMLGregorianCalendar</code>.</p>
2812         *
2813         * <p>If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value,
2814         * an <code>IllegalArgumentException</code> is thrown.</p>
2815         *
2816         * @throws IllegalArgumentException If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value.
2817         */
2818        public void parse() throws IllegalArgumentException {
2819            while (fidx < flen) {
2820                char fch = format.charAt(fidx++);
2821
2822                if (fch != '%') { // not a meta character
2823                    skip(fch);
2824                    continue;
2825                }
2826
2827                // seen meta character. we don't do error check against the format
2828                switch (format.charAt(fidx++)) {
2829                    case 'Y' : // year
2830                        parseYear();
2831                        break;
2832
2833                    case 'M' : // month
2834                        setMonth(parseInt(2, 2));
2835                        break;
2836
2837                    case 'D' : // days
2838                        setDay(parseInt(2, 2));
2839                        break;
2840
2841                    case 'h' : // hours
2842                        setHour(parseInt(2, 2), false);
2843                        break;
2844
2845                    case 'm' : // minutes
2846                        setMinute(parseInt(2, 2));
2847                        break;
2848
2849                    case 's' : // parse seconds.
2850                        setSecond(parseInt(2, 2));
2851
2852                        if (peek() == '.') {
2853                            setFractionalSecond(parseBigDecimal());
2854                        }
2855                        break;
2856
2857                    case 'z' : // time zone. missing, 'Z', or [+-]nn:nn
2858                        char vch = peek();
2859                        if (vch == 'Z') {
2860                            vidx++;
2861                            setTimezone(0);
2862                        } else if (vch == '+' || vch == '-') {
2863                            vidx++;
2864                            int h = parseInt(2, 2);
2865                            skip(':');
2866                            int m = parseInt(2, 2);
2867                            setTimezone((h * 60 + m) * (vch == '+' ? 1 : -1));
2868                        }
2869
2870                        break;
2871
2872                    default :
2873                        // illegal meta character. impossible.
2874                        throw new InternalError();
2875                }
2876            }
2877
2878            if (vidx != vlen) {
2879                // some tokens are left in the input
2880                throw new IllegalArgumentException(value); //,vidx);
2881            }
2882            testHour();
2883        }
2884
2885        private char peek() throws IllegalArgumentException {
2886            if (vidx == vlen) {
2887                return (char) -1;
2888            }
2889            return value.charAt(vidx);
2890        }
2891
2892        private char read() throws IllegalArgumentException {
2893            if (vidx == vlen) {
2894                throw new IllegalArgumentException(value); //,vidx);
2895            }
2896            return value.charAt(vidx++);
2897        }
2898
2899        private void skip(char ch) throws IllegalArgumentException {
2900            if (read() != ch) {
2901                throw new IllegalArgumentException(value); //,vidx-1);
2902            }
2903        }
2904
2905        private int parseInt(int minDigits, int maxDigits)
2906            throws IllegalArgumentException {
2907
2908            int n = 0;
2909            char ch;
2910            int vstart = vidx;
2911            while (isDigit(ch=peek()) && (vidx - vstart) < maxDigits) {
2912                vidx++;
2913                n = n*10 + ch-'0';
2914            }
2915            if ((vidx - vstart) < minDigits) {
2916                // we are expecting more digits
2917                throw new IllegalArgumentException(value); //,vidx);
2918            }
2919
2920            return n;
2921        }
2922
2923        private void parseYear()
2924            throws IllegalArgumentException {
2925            int vstart = vidx;
2926            int sign = 0;
2927
2928            // skip leading negative, if it exists
2929            if (peek() == '-') {
2930                vidx++;
2931                sign = 1;
2932            }
2933            while (isDigit(peek())) {
2934                vidx++;
2935            }
2936            final int digits = vidx - vstart - sign;
2937            if (digits < 4) {
2938                // we are expecting more digits
2939                throw new IllegalArgumentException(value); //,vidx);
2940            }
2941            final String yearString = value.substring(vstart, vidx);
2942            if (digits < 10) {
2943                setYear(Integer.parseInt(yearString));
2944            }
2945            else {
2946                setYear(new BigInteger(yearString));
2947            }
2948        }
2949
2950        private BigDecimal parseBigDecimal()
2951                throws IllegalArgumentException {
2952            int vstart = vidx;
2953
2954            if (peek() == '.') {
2955                vidx++;
2956            } else {
2957                throw new IllegalArgumentException(value);
2958            }
2959            while (isDigit(peek())) {
2960                vidx++;
2961            }
2962            return new BigDecimal(value.substring(vstart, vidx));
2963        }
2964    }
2965
2966    private static boolean isDigit(char ch) {
2967        return '0' <= ch && ch <= '9';
2968    }
2969
2970    /**
2971     * Prints this object according to the format specification.
2972     *
2973     * <p>
2974     * StringBuffer -> StringBuilder change had a very visible impact.
2975     * It almost cut the execution time to half.
2976     * Diff from Xerces:
2977     * Xerces use StringBuffer due to the requirement to support
2978     * JDKs older than JDK 1.5
2979     */
2980    private String format( String format ) {
2981        StringBuilder buf = new StringBuilder();
2982        int fidx=0,flen=format.length();
2983
2984        while(fidx<flen) {
2985            char fch = format.charAt(fidx++);
2986            if(fch!='%') {// not a meta char
2987                buf.append(fch);
2988                continue;
2989            }
2990
2991            switch(format.charAt(fidx++)) {
2992                case 'Y':
2993                    if (eon == null) {
2994                        int absYear = year;
2995                        if (absYear < 0) {
2996                            buf.append('-');
2997                            absYear = -year;
2998                        }
2999                        printNumber(buf, absYear, 4);
3000                    }
3001                    else {
3002                        printNumber(buf, getEonAndYear(), 4);
3003                    }
3004                    break;
3005                case 'M':
3006                    printNumber(buf,getMonth(),2);
3007                    break;
3008                case 'D':
3009                    printNumber(buf,getDay(),2);
3010                    break;
3011                case 'h':
3012                    printNumber(buf,getHour(),2);
3013                    break;
3014                case 'm':
3015                    printNumber(buf,getMinute(),2);
3016                    break;
3017                case 's':
3018                    printNumber(buf,getSecond(),2);
3019                    if (getFractionalSecond() != null) {
3020                        //Xerces uses a custom method toString instead of
3021                        //toPlainString() since it needs to support JDKs older than 1.5
3022                        String frac = getFractionalSecond().toPlainString();
3023                        //skip leading zero.
3024                        buf.append(frac.substring(1, frac.length()));
3025                    }
3026                    break;
3027                case 'z':
3028                    int offset = getTimezone();
3029                    if (offset == 0) {
3030                        buf.append('Z');
3031                    }
3032                    else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
3033                        if (offset < 0) {
3034                            buf.append('-');
3035                            offset *= -1;
3036                        }
3037                        else {
3038                            buf.append('+');
3039                        }
3040                        printNumber(buf,offset/60,2);
3041                        buf.append(':');
3042                        printNumber(buf,offset%60,2);
3043                    }
3044                    break;
3045                default:
3046                    throw new InternalError();  // impossible
3047            }
3048        }
3049
3050        return buf.toString();
3051    }
3052
3053    /**
3054     * Prints an integer as a String.
3055     *
3056     * @param out
3057     *      The formatted string will be appended into this buffer.
3058     * @param number
3059     *      The integer to be printed.
3060     * @param nDigits
3061     *      The field will be printed by using at least this
3062     *      number of digits. For example, 5 will be printed as "0005"
3063     *      if nDigits==4.
3064     */
3065    private void printNumber( StringBuilder out, int number, int nDigits ) {
3066        String s = String.valueOf(number);
3067        for (int i = s.length(); i < nDigits; i++) {
3068            out.append('0');
3069        }
3070        out.append(s);
3071    }
3072
3073    /**
3074     * Prints an BigInteger as a String.
3075     *
3076     * @param out
3077     *      The formatted string will be appended into this buffer.
3078     * @param number
3079     *      The integer to be printed.
3080     * @param nDigits
3081     *      The field will be printed by using at least this
3082     *      number of digits. For example, 5 will be printed as "0005"
3083     *      if nDigits==4.
3084     */
3085    private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
3086        String s = number.toString();
3087        for (int i=s.length(); i < nDigits; i++) {
3088            out.append('0');
3089        }
3090        out.append(s);
3091    }
3092
3093    /**
3094     * Compute <code>value*signum</code> where value==null is treated as
3095     * value==0.
3096     * @return non-null {@link BigInteger}.
3097     */
3098    static BigInteger sanitize(Number value, int signum) {
3099        if (signum == 0 || value == null) {
3100            return BigInteger.ZERO;
3101        }
3102        return (signum <  0)? ((BigInteger)value).negate() : (BigInteger)value;
3103    }
3104
3105    /** <p><code>reset()</code> is designed to allow the reuse of existing
3106     * <code>XMLGregorianCalendar</code>s thus saving resources associated
3107     *  with the creation of new <code>XMLGregorianCalendar</code>s.</p>
3108     */
3109    public void reset() {
3110        eon = orig_eon;
3111        year = orig_year;
3112        month = orig_month;
3113        day = orig_day;
3114        hour = orig_hour;
3115        minute = orig_minute;
3116        second = orig_second;
3117        fractionalSecond = orig_fracSeconds;
3118        timezone = orig_timezone;
3119    }
3120
3121    /** Deserialize Calendar. */
3122    private void readObject(ObjectInputStream ois)
3123        throws ClassNotFoundException, IOException {
3124
3125        // perform default deseralization
3126        ois.defaultReadObject();
3127
3128        // initialize orig_* fields
3129        save();
3130
3131    } // readObject(ObjectInputStream)
3132}
3133