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