KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > jaxp > datatype > XMLGregorianCalendarImpl


1 // $Id: XMLGregorianCalendarImpl.java,v 1.11 2004/07/07 06:16:15 jsuttor Exp $
2

3 /*
4  * @(#)XMLGregorianCalendarImpl.java 1.4 04/07/26
5  *
6  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
7  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
8  */

9
10 package com.sun.org.apache.xerces.internal.jaxp.datatype;
11
12 import java.io.Serializable JavaDoc;
13 import java.math.BigDecimal JavaDoc;
14 import java.math.BigInteger JavaDoc;
15 import java.util.TimeZone JavaDoc;
16 import java.util.SimpleTimeZone JavaDoc;
17 import java.util.Calendar JavaDoc;
18 import java.util.GregorianCalendar JavaDoc;
19 import java.util.Date JavaDoc;
20 import java.util.Locale JavaDoc;
21
22 import javax.xml.XMLConstants JavaDoc;
23 import javax.xml.datatype.DatatypeConstants JavaDoc;
24 import javax.xml.datatype.Duration JavaDoc;
25 import javax.xml.datatype.XMLGregorianCalendar JavaDoc;
26 import javax.xml.namespace.QName JavaDoc;
27 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
28
29
30 /**
31  * <p>Representation for W3C XML Schema 1.0 date/time datatypes.
32  * Specifically, these date/time datatypes are
33  * {@link DatatypeConstants#DATETIME dateTime},
34  * {@link DatatypeConstants#TIME time},
35  * {@link DatatypeConstants#DATE date},
36  * {@link DatatypeConstants#GYEARMONTH gYearMonth},
37  * {@link DatatypeConstants#GMONTHDAY gMonthDay},
38  * {@link DatatypeConstants#GYEAR gYear},
39  * {@link DatatypeConstants#GMONTH gMonth} and
40  * {@link DatatypeConstants#GDAY gDay}
41  * defined in the XML Namespace
42  * <code>"http://www.w3.org/2001/XMLSchema"</code>.
43  * These datatypes are normatively defined in
44  * <a HREF="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML Schema 1.0 Part 2, Section 3.2.7-14</a>.</p>
45  *
46  * <p>The table below defines the mapping between XML Schema 1.0
47  * date/time datatype fields and this class' fields. It also summarizes
48  * the value constraints for the date and time fields defined in
49  * <a HREF="http://www.w3.org/TR/xmlschema-2/#isoformats">W3C XML Schema 1.0 Part 2, Appendix D,
50  * <i>ISO 8601 Date and Time Formats</i></a>.</p>
51  *
52  * <a name="datetimefieldsmapping"/>
53  * <table border="2" rules="all" cellpadding="2">
54  * <thead>
55  * <tr>
56  * <th align="center" colspan="3">
57  * Date/time datatype field mapping between XML Schema 1.0 and Java representation
58  * </th>
59  * </tr>
60  * </thead>
61  * <tbody>
62  * <tr>
63  * <th>XML Schema 1.0<br/>
64  * datatype<br/>
65  * field</th>
66  * <th>Related<br/>XMLGregorianCalendar<br/>Accessor(s)</th>
67  * <th>Value Range</th>
68  * </tr>
69  * <a name="datetimefield-year"/>
70  * <tr>
71  * <td> year </td>
72  * <td> {@link #getYear()} + {@link #getEon()} or<br/>
73  * {@link #getEonAndYear}
74  * </td>
75  * <td> <code>getYear()</code> is a value between -(10^9-1) to (10^9)-1
76  * or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
77  * {@link #getEon()} is high order year value in billion of years.<br/>
78  * <code>getEon()</code> has values greater than or equal to (10^9) or less than or equal to -(10^9).
79  * A value of null indicates field is undefined.</br>
80  * 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
81  * will be a valid lexical value in a future version of XML Schema,
82  * this class allows the year field to be set to zero. Otherwise,
83  * the year field value is handled exactly as described
84  * in the errata and [ISO-8601-1988]. Note that W3C XML Schema 1.0
85  * validation does not allow for the year field to have a value of zero.
86  * </td>
87  * </tr>
88  * <a name="datetimefield-month"/>
89  * <tr>
90  * <td> month </td>
91  * <td> {@link #getMonth()} </td>
92  * <td> 1 to 12 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
93  * </tr>
94  * <a name="datetimefield-day"/>
95  * <tr>
96  * <td> day </td>
97  * <td> {@link #getDay()} </td>
98  * <td> Independent of month, max range is 1 to 31 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
99  * The normative value constraint stated relative to month
100  * 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>.
101  * </td>
102  * </tr>
103  * <a name="datetimefield-hour"/>
104  * <tr>
105  * <td> hour </td>
106  * <td> {@link #getHour()} </td>
107  * <td>
108  * 0 to 24 or {@link DatatypeConstants#FIELD_UNDEFINED}
109  * <a HREF="http://www.w3.org/2001/05/xmlschema-errata#e2-45">For a value of 24, the minute and second field must be zero.</a>
110  * </td>
111  * </tr>
112  * <a name="datetimefield-minute"/>
113  * <tr>
114  * <td> minute </td>
115  * <td> {@link #getMinute()} </td>
116  * <td> 0 to 59 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
117  * </tr>
118  * <a name="datetimefield-second"/>
119  * <tr>
120  * <td>second</td>
121  * <td>
122  * {@link #getSecond()} + {@link #getMillisecond()}/1000 or<br/>
123  * {@link #getSecond()} + {@link #getFractionalSecond()}
124  * </td>
125  * <td>
126  * {@link #getSecond()} from 0 to 60 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
127  * <i>(Note: 60 only allowable for leap second.)</i><br/>
128  * {@link #getFractionalSecond()} allows for infinite precision over the range from 0.0 to 1.0 when
129  * the {@link #getSecond()} is defined.<br/>
130  * <code>FractionalSecond</code> is optional and has a value of <code>null</code> when it is undefined.<br />
131  * {@link #getMillisecond()} is the convenience
132  * millisecond precision of value of {@link #getFractionalSecond()}.
133  * </td>
134  * </tr>
135  * <tr id="datetimefield-timezone">
136  * <td> timezone </td>
137  * <td> {@link #getTimezone()} </td>
138  * <td> Number of minutes or {@link DatatypeConstants#FIELD_UNDEFINED}.
139  * Value range from -14 hours (-14 * 60 minutes) to 14 hours (14 * 60 minutes).
140  * </td>
141  * </tr>
142  * </tbody>
143  * </table>
144  *
145  * <p>All maximum value space constraints listed for the fields in the table
146  * above are checked by factory methods, setter methods and parse methods of
147  * this class. <code>IllegalArgumentException</code> is thrown when
148  * parameter's value is outside the maximum value constraint for the field.
149  * Validation checks, for example, whether days in month should be
150  * limited to 29, 30 or 31 days, that are dependent on the values of other
151  * fields are not checked by these methods.
152  * </p>
153  *
154  * <p>The following operations are defined for this class:
155  * <ul>
156  * <li>factory methods to create instances</li>
157  * <li>accessors/mutators for independent date/time fields</li>
158  * <li>conversion between this class and W3C XML Schema 1.0 lexical representation</li>
159  * <li>conversion between this class and <code>java.util.GregorianCalendar</code></li>
160  * <li>partial order relation comparator method, {@link #compare(XMLGregorianCalendar)}</li>
161  * <li>{@link #equals(Object)} defined relative to {@link #compare(XMLGregorianCalendar)}.</li>
162  * <li> addition operation with {@link javax.xml.datatype.Duration}.
163  * instance as defined in <a HREF="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">
164  * W3C XML Schema 1.0 Part 2, Appendix E, <i>Adding durations to dateTimes</i></a>.</li>
165  * </ul>
166  * </p>
167  *
168  * @author <a HREF="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
169  * @author <a HREF="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
170  * @version $Revision: 1.11 $, $Date: 2004/07/07 06:16:15 $
171  * @see javax.xml.datatype.Duration
172  * @since 1.5
173  */

174
175 public class XMLGregorianCalendarImpl
176     extends XMLGregorianCalendar JavaDoc
177     implements Serializable JavaDoc, Cloneable JavaDoc {
178
179     /**
180      * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
181      */

182     private BigInteger JavaDoc eon = null;
183     
184     /**
185      * <p>Year of this <code>XMLGregorianCalendar</code>.</p>
186      */

187     private int year = DatatypeConstants.FIELD_UNDEFINED;
188
189     /**
190      * <p>Month of this <code>XMLGregorianCalendar</code>.</p>
191      */

192     private int month = DatatypeConstants.FIELD_UNDEFINED;
193     
194     /**
195      * <p>Day of this <code>XMLGregorianCalendar</code>.</p>
196      */

197     private int day = DatatypeConstants.FIELD_UNDEFINED;
198     
199     /**
200      * <p>Timezone of this <code>XMLGregorianCalendar</code> in minutes.</p>
201      */

202     private int timezone = DatatypeConstants.FIELD_UNDEFINED;
203     
204     /**
205      * <p>Hour of this <code>XMLGregorianCalendar</code>.</p>
206      */

207     private int hour = DatatypeConstants.FIELD_UNDEFINED;
208     
209     /**
210      * <p>Minute of this <code>XMLGregorianCalendar</code>.</p>
211      */

212     private int minute = DatatypeConstants.FIELD_UNDEFINED;
213     
214     /**
215      * <p>Second of this <code>XMLGregorianCalendar</code>.</p>
216      */

217     private int second = DatatypeConstants.FIELD_UNDEFINED ;
218     
219     /**
220      * <p>Fractional second of this <code>XMLGregorianCalendar</code>.</p>
221      */

222     private BigDecimal JavaDoc fractionalSecond = null;
223
224     /**
225      * <p>Constant to represent a billion.</p>
226      */

227     private static final BigInteger JavaDoc BILLION = new BigInteger JavaDoc("1000000000");
228
229     /**
230      * <p>Obtain a pure Gregorian Calendar by calling
231      * GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE). </p>
232      */

233     private static final Date JavaDoc PURE_GREGORIAN_CHANGE =
234     new Date JavaDoc(Long.MIN_VALUE);
235
236     /**
237      * Year index for MIN_ and MAX_FIELD_VALUES.
238      */

239     private static final int YEAR = 0;
240
241     /**
242      * Month index for MIN_ and MAX_FIELD_VALUES.
243      */

244     private static final int MONTH = 1;
245
246     /**
247      * Day index for MIN_ and MAX_FIELD_VALUES.
248      */

249     private static final int DAY = 2;
250
251     /**
252      * Hour index for MIN_ and MAX_FIELD_VALUES.
253      */

254     private static final int HOUR = 3;
255
256     /**
257      * Minute index for MIN_ and MAX_FIELD_VALUES.
258      */

259     private static final int MINUTE = 4;
260
261     /**
262      * Second index for MIN_ and MAX_FIELD_VALUES.
263      */

264     private static final int SECOND = 5;
265
266     /**
267      * Second index for MIN_ and MAX_FIELD_VALUES.
268      */

269     private static final int MILLISECOND = 6;
270
271     /**
272      * Timezone index for MIN_ and MAX_FIELD_VALUES
273      */

274     private static final int TIMEZONE = 7;
275
276     /**
277      * Minimum field values indexed by YEAR..TIMEZONE.
278      */

279     private static final int MIN_FIELD_VALUE[] = {
280     Integer.MIN_VALUE, //Year field can be smaller than this,
281
// only constraint on integer value of year.
282
DatatypeConstants.JANUARY,
283     1, //day of month
284
0, //hour
285
0, //minute
286
0, //second
287
0, //millisecond
288
-14 * 60 //timezone
289
};
290
291     /**
292      * Maximum field values indexed by YEAR..TIMEZONE.
293      */

294     private static final int MAX_FIELD_VALUE[] = {
295     Integer.MAX_VALUE, // Year field can be bigger than this,
296
// only constraint on integer value of year.
297
DatatypeConstants.DECEMBER,
298     31, //day of month
299
23, //hour
300
59, //minute
301
60, //second (leap second allows for 60)
302
999, //millisecond
303
14 * 60 //timezone
304
};
305
306     /**
307      * field names indexed by YEAR..TIMEZONE.
308      */

309     private static final String JavaDoc FIELD_NAME[] = {
310     "Year",
311     "Month",
312     "Day",
313     "Hour",
314     "Minute",
315     "Second",
316     "Millisecond",
317     "Timezone"
318     };
319
320     /**
321      * <p>Stream Unique Identifier.</p>
322      *
323      * <p>TODO: Serialization should use the XML string representation as
324      * the serialization format to ensure future compatibility.</p>
325      */

326     private static final long serialVersionUID = 1L;
327
328     /**
329      * <p>Use as a template for default field values when
330      * converting to a {@link GregorianCalendar}, set to a leap
331      * year date of January 1, 0400 at midnight.</p>
332      *
333      * <p>Fields that are optional for an <code>xsd:dateTime</code> instances are defaulted to not being set to any value.
334      * <code>XMLGregorianCalendar</code> fields millisecond, fractional second and timezone return the value indicating
335      * that the field is not set, {@link DatatypeConstants#FIELD_UNDEFINED} for millisecond and timezone
336      * and <code>null</code> for fractional second.</p>
337      *
338      * @see #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
339      */

340     public static final XMLGregorianCalendar JavaDoc LEAP_YEAR_DEFAULT =
341         createDateTime(
342             400, //year
343
DatatypeConstants.JANUARY, //month
344
1, // day
345
0, // hour
346
0, // minute
347
0, // second
348
DatatypeConstants.FIELD_UNDEFINED, // milliseconds
349
DatatypeConstants.FIELD_UNDEFINED // timezone
350
);
351
352     // Constructors
353

354     /**
355      * Constructs a new XMLGregorianCalendar object.
356      *
357      * String parsing documented by {@link #parse(String)}.
358      *
359      * Returns a non-null valid XMLGregorianCalendar object that holds the
360      * value indicated by the lexicalRepresentation parameter.
361      *
362      * @param lexicalRepresentation
363      * Lexical representation of one the eight
364      * XML Schema date/time datatypes.
365      * @throws IllegalArgumentException
366      * If the given string does not conform as documented in
367      * {@link #parse(String)}.
368      * @throws NullPointerException
369      * If the given string is null.
370      */

371     protected XMLGregorianCalendarImpl(String JavaDoc lexicalRepresentation)
372         throws IllegalArgumentException JavaDoc {
373             
374     // compute format string for this lexical representation.
375
String JavaDoc format = null;
376     String JavaDoc lexRep = lexicalRepresentation;
377     final int NOT_FOUND = -1;
378     int lexRepLength = lexRep.length();
379     
380     
381     // current parser needs a format string,
382
// use following heuristics to figure out what xml schema date/time
383
// datatype this lexical string could represent.
384
if (lexRep.indexOf('T') != NOT_FOUND) {
385         // found Date Time separater, must be xsd:DateTime
386
format = "%Y-%M-%DT%h:%m:%s" + "%z";
387     } else if (lexRepLength >= 3 && lexRep.charAt(2) == ':') {
388         // found ":", must be xsd:Time
389
format = "%h:%m:%s" +"%z";
390     } else if (lexRep.startsWith("--")) {
391         // check for GDay || GMonth || GMonthDay
392
if (lexRepLength >= 3 && lexRep.charAt(2) == '-') {
393         // GDAY
394
// Fix 4971612: invalid SCCS macro substitution in data string
395
format = "---%D" + "%z";
396         } else if (lexRepLength >= 6 &&
397                lexRep.charAt(5) == '-' && lexRep.charAt(4) == '-') {
398         // GMonth
399
// Fix 4971612: invalid SCCS macro substitution in data string
400
format = "--%M--%z";
401         } else {
402         // GMonthDay or invalid lexicalRepresentation
403
format = "--%M-%D" + "%z";
404         }
405     } else {
406         // check for Date || GYear | GYearMonth
407
int countSeparator = 0;
408
409         // start at index 1 to skip potential negative sign for year.
410

411         
412         int timezoneOffset = lexRep.indexOf(':');
413         if (timezoneOffset != NOT_FOUND) {
414
415         // found timezone, strip it off for distinguishing
416
// between Date, GYear and GYearMonth so possible
417
// negative sign in timezone is not mistaken as
418
// a separator.
419
lexRepLength -= 6;
420         }
421         
422         for (int i=1; i < lexRepLength; i++) {
423         if (lexRep.charAt(i) == '-') {
424             countSeparator++;
425         }
426         }
427         if (countSeparator == 0) {
428         // GYear
429
format = "%Y" + "%z";
430         } else if (countSeparator == 1) {
431         // GYearMonth
432
format = "%Y-%M" + "%z";
433         } else {
434         // Date or invalid lexicalRepresentation
435
// Fix 4971612: invalid SCCS macro substitution in data string
436
format = "%Y-%M-%D" + "%z";
437         }
438     }
439     Parser p = new Parser(format, lexRep);
440     p.parse();
441     
442         // check for validity
443
if (!isValid()) {
444             throw new IllegalArgumentException JavaDoc(
445                 DatatypeMessageFormatter.formatMessage(null,"InvalidXGCRepresentation", new Object JavaDoc[]{lexicalRepresentation})
446                 //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
447
);
448         }
449     }
450
451     /**
452      * <p>Create an instance with all date/time datatype fields set to
453      * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.</p>
454      */

455     public XMLGregorianCalendarImpl() {
456         
457         // field initializers already do the correct initialization.
458
}
459
460     /**
461      * <p>Private constructor allowing for complete value spaces allowed by
462      * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
463      * builtin datatypes. Note that <code>year</code> parameter supports
464      * arbitrarily large numbers and fractionalSecond has infinite
465      * precision.</p>
466      *
467      * @param year of <code>XMLGregorianCalendar</code> to be created.
468      * @param month of <code>XMLGregorianCalendar</code> to be created.
469      * @param day of <code>XMLGregorianCalendar</code> to be created.
470      * @param hour of <code>XMLGregorianCalendar</code> to be created.
471      * @param minute of <code>XMLGregorianCalendar</code> to be created.
472      * @param second of <code>XMLGregorianCalendar</code> to be created.
473      * @param fractionalSecond of <code>XMLGregorianCalendar</code> to be created.
474      * @param timezone of <code>XMLGregorianCalendar</code> to be created.
475      *
476      */

477     protected XMLGregorianCalendarImpl(
478         BigInteger JavaDoc year,
479         int month,
480         int day,
481         int hour,
482         int minute,
483         int second,
484         BigDecimal JavaDoc fractionalSecond,
485         int timezone) {
486             
487         setYear(year);
488         setMonth(month);
489         setDay(day);
490         setTime(hour, minute, second, fractionalSecond);
491         setTimezone(timezone);
492         
493         // check for validity
494
if (!isValid()) {
495             
496             throw new IllegalArgumentException JavaDoc(
497                 DatatypeMessageFormatter.formatMessage(null,
498                     "InvalidXGCValue-fractional",
499                     new Object JavaDoc[] { year, new Integer JavaDoc(month), new Integer JavaDoc(day),
500                     new Integer JavaDoc(hour), new Integer JavaDoc(minute), new Integer JavaDoc(second),
501                     fractionalSecond, new Integer JavaDoc(timezone)})
502             );
503                     
504             /**
505                 String yearString = "null";
506                 if (year != null) {
507                     yearString = year.toString();
508                 }
509                 String fractionalSecondString = "null";
510                 if (fractionalSecond != null) {
511                     fractionalSecondString = fractionalSecond.toString();
512                 }
513              
514                 throw new IllegalArgumentException(
515                     "year = " + yearString
516                     + ", month = " + month
517                     + ", day = " + day
518                     + ", hour = " + hour
519                     + ", minute = " + minute
520                     + ", second = " + second
521                     + ", fractionalSecond = " + fractionalSecondString
522                     + ", timezone = " + timezone
523                     + ", is not a valid representation of an XML Gregorian Calendar value."
524                 );
525                 */

526             
527         }
528         
529     }
530
531     /**
532      * <p>Private constructor of value spaces that a
533      * <code>java.util.GregorianCalendar</code> instance would need to convert to an
534      * <code>XMLGregorianCalendar</code> instance.</p>
535      *
536      * <p><code>XMLGregorianCalendar eon</code> and
537      * <code>fractionalSecond</code> are set to <code>null</code></p>
538      *
539      * @param year of <code>XMLGregorianCalendar</code> to be created.
540      * @param month of <code>XMLGregorianCalendar</code> to be created.
541      * @param day of <code>XMLGregorianCalendar</code> to be created.
542      * @param hour of <code>XMLGregorianCalendar</code> to be created.
543      * @param minute of <code>XMLGregorianCalendar</code> to be created.
544      * @param second of <code>XMLGregorianCalendar</code> to be created.
545      * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
546      * @param timezone of <code>XMLGregorianCalendar</code> to be created.
547      */

548     private XMLGregorianCalendarImpl(
549         int year,
550         int month,
551         int day,
552         int hour,
553         int minute,
554         int second,
555         int millisecond,
556         int timezone) {
557             
558         setYear(year);
559         setMonth(month);
560         setDay(day);
561         setTime(hour, minute, second);
562         setTimezone(timezone);
563         setMillisecond(millisecond);
564
565         if (!isValid()) {
566             
567             throw new IllegalArgumentException JavaDoc(
568                 DatatypeMessageFormatter.formatMessage(null,
569                 "InvalidXGCValue-milli",
570                 new Object JavaDoc[] { new Integer JavaDoc(year), new Integer JavaDoc(month), new Integer JavaDoc(day),
571                 new Integer JavaDoc(hour), new Integer JavaDoc(minute), new Integer JavaDoc(second),
572                 new Integer JavaDoc(millisecond), new Integer JavaDoc(timezone)})
573             );
574                 /*
575                 throw new IllegalArgumentException(
576                     "year = " + year
577                     + ", month = " + month
578                     + ", day = " + day
579                     + ", hour = " + hour
580                     + ", minute = " + minute
581                     + ", second = " + second
582                     + ", millisecond = " + millisecond
583                     + ", timezone = " + timezone
584                     + ", is not a valid representation of an XML Gregorian Calendar value."
585                     );
586                  */

587             
588         }
589     }
590     
591     /**
592      * <p>Convert a <code>java.util.GregorianCalendar</code> to XML Schema 1.0
593      * representation.</p>
594      *
595      * <table border="2" rules="all" cellpadding="2">
596      * <thead>
597      * <tr>
598      * <th align="center" colspan="2">
599      * Field by Field Conversion from
600      * <code>java.util.GregorianCalendar</code> to this class
601      * </th>
602      * </tr>
603      * </thead>
604      * <tbody>
605      * <tr>
606      * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
607      * <th><code>java.util.GregorianCalendar</code> field</th>
608      * </tr>
609      * <tr>
610      * <th>{@link #setYear(int)}</th>
611      * <th><code>ERA == GregorianCalendar.BC ? -YEAR : YEAR</code></th>
612      * </tr>
613      * <tr>
614      * <th>{@link #setMonth(int)}</th>
615      * <th><code>MONTH + 1</code></th>
616      * </tr>
617      * <tr>
618      * <th>{@link #setDay(int)}</th>
619      * <th><code>DAY_OF_MONTH</code></th>
620      * </tr>
621      * <tr>
622      * <th>{@link #setTime(int,int,int, BigDecimal)}</th>
623      * <th><code>HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND</code></th>
624      * </tr>
625      * <tr>
626      * <th>{@link #setTimezone(int)}<i>*</i></th>
627      * <th><code>(ZONE_OFFSET + DST_OFFSET) / (60*1000)</code><br/>
628      * <i>(in minutes)</i>
629      * </th>
630      * </tr>
631      * </tbody>
632      * </table>
633      * <p><i>*</i>conversion loss of information. It is not possible to represent
634      * a <code>java.util.GregorianCalendar</code> daylight savings timezone id in the
635      * XML Schema 1.0 date/time datatype representation.</p>
636      *
637      * <p>To compute the return value's <code>TimeZone</code> field,
638      * <ul>
639      * <li>when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
640      * create a <code>java.util.TimeZone</code> with a custom timezone id
641      * using the <code>this.getTimezone()</code>.</li>
642      * <li>else use the <code>GregorianCalendar</code> default timezone value
643      * for the host is defined as specified by
644      * <code>java.util.TimeZone.getDefault()</code>.</li></p>
645      *
646      * @param cal <code>java.util.GregorianCalendar</code> used to create <code>XMLGregorianCalendar</code>
647      */

648     public XMLGregorianCalendarImpl(GregorianCalendar JavaDoc cal) {
649         
650     int year = cal.get(Calendar.YEAR);
651     if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
652         year = -year;
653     }
654     this.setYear(year);
655     
656     // Calendar.MONTH is zero based, XSD Date datatype's month field starts
657
// with JANUARY as 1.
658
this.setMonth(cal.get(Calendar.MONTH) + 1);
659     this.setDay(cal.get(Calendar.DAY_OF_MONTH));
660     this.setTime(
661         cal.get(Calendar.HOUR_OF_DAY),
662         cal.get(Calendar.MINUTE),
663         cal.get(Calendar.SECOND),
664         cal.get(Calendar.MILLISECOND));
665     
666     // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
667
int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
668     this.setTimezone(offsetInMinutes);
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      */

694     public static XMLGregorianCalendar JavaDoc createDateTime(
695         BigInteger JavaDoc year,
696         int month,
697         int day,
698         int hours,
699         int minutes,
700         int seconds,
701         BigDecimal JavaDoc 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      */

732     public static XMLGregorianCalendar JavaDoc 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      */

772     public static XMLGregorianCalendar JavaDoc 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      */

813     public static XMLGregorianCalendar JavaDoc 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      */

845     public static XMLGregorianCalendar JavaDoc 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      */

879     public static XMLGregorianCalendar JavaDoc createTime(
880         int hours,
881         int minutes,
882         int seconds,
883         BigDecimal JavaDoc 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      */

914     public static XMLGregorianCalendar JavaDoc 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      */

947     public BigInteger JavaDoc 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      */

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      */

982     public BigInteger JavaDoc 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     */

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     */

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     */

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     */

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     */

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     */

1083    public int getSecond() {
1084       return second;
1085    }
1086
1087    /**
1088     * @return result of adding second and fractional second field
1089     */

1090    private BigDecimal JavaDoc getSeconds() {
1091        
1092    if (second == DatatypeConstants.FIELD_UNDEFINED) {
1093        return DECIMAL_ZERO;
1094    }
1095    BigDecimal JavaDoc result = BigDecimal.valueOf((long)second);
1096    if (fractionalSecond != null){
1097        return result.add(fractionalSecond);
1098    } else {
1099        return result;
1100    }
1101    }
1102
1103
1104    /**
1105     * <p>Return millisecond precision of {@link #getFractionalSecond()}.<\p>
1106     *
1107     * <p>This method represents a convenience accessor to infinite
1108     * precision fractional second value returned by
1109     * {@link #getFractionalSecond()}. The returned value is the rounded
1110     * down to milliseconds value of
1111     * {@link #getFractionalSecond()}. When {@link #getFractionalSecond()}
1112     * returns <code>null</code>, this method must return
1113     * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1114     *
1115     * <p>Value constraints for this value are summarized in
1116     * <a HREF="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1117     *
1118     * @return Millisecond of this <code>XMLGregorianCalendar</code>.
1119     *
1120     * @see #getFractionalSecond()
1121     * @see #setTime(int, int, int)
1122     */

1123    public int getMillisecond() {
1124        if (fractionalSecond == null) {
1125            return DatatypeConstants.FIELD_UNDEFINED;
1126        } else {
1127            // TODO: Non-optimal solution for now.
1128
// Efficient implementation would only store as BigDecimal
1129
// when needed and millisecond otherwise.
1130
return fractionalSecond.movePointRight(3).intValue();
1131        }
1132    }
1133
1134    /**
1135     * <p>Return fractional seconds.</p>
1136     *
1137     * <p><code>null</code> is returned when this optional field is not defined.</p>
1138     *
1139     * <p>Value constraints are detailed in
1140     * <a HREF="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1141     *
1142     * <p>This optional field can only have a defined value when the
1143     * xs:dateTime second field, represented by ({@link #getSecond()},
1144     * does not return {@link DatatypeConstants#FIELD_UNDEFINED}).</p>
1145     *
1146     * @return fractional seconds of this <code>XMLGregorianCalendar</code>.
1147     *
1148     * @see #getSecond()
1149     * @see #setTime(int, int, int, BigDecimal)
1150     */

1151    public BigDecimal JavaDoc getFractionalSecond() {
1152       return fractionalSecond;
1153    }
1154
1155    // setters
1156

1157    /**
1158     * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
1159     *
1160     * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
1161     *
1162     * @param year value constraints summarized in <a HREF="#datetimefield-year">year field of date/time field mapping table</a>.
1163     *
1164     * @throws IllegalArgumentException if <code>year</code> parameter is
1165     * outside value constraints for the field as specified in
1166     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1167     */

1168    public void setYear(BigInteger JavaDoc year) {
1169    if (year == null) {
1170        this.eon = null;
1171        this.year = DatatypeConstants.FIELD_UNDEFINED;
1172    } else {
1173        BigInteger JavaDoc temp = year.remainder(BILLION);
1174        this.year = temp.intValue();
1175        setEon(year.subtract(temp));
1176    }
1177    }
1178
1179    /**
1180     * <p>Set year of XSD <code>dateTime</code> year field.</p>
1181     *
1182     * <p>Unset this field by invoking the setter with a parameter value of
1183     * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1184     *
1185     * <p>Note: if the absolute value of the <code>year</code> parameter
1186     * is less than 10^9, the eon component of the XSD year field is set to
1187     * <code>null</code> by this method.</p>
1188     *
1189     * @param year value constraints are summarized in <a HREF="#datetimefield-year">year field of date/time field mapping table</a>.
1190     * If year is {@link DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
1191     */

1192    public void setYear(int year) {
1193    if (year == DatatypeConstants.FIELD_UNDEFINED) {
1194        this.year = DatatypeConstants.FIELD_UNDEFINED;
1195        this.eon = null;
1196    } else if (Math.abs(year) < BILLION.intValue()) {
1197        this.year = year;
1198        this.eon = null;
1199    } else {
1200        BigInteger JavaDoc theYear = BigInteger.valueOf((long) year);
1201        BigInteger JavaDoc remainder = theYear.remainder(BILLION);
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     */

1215    private void setEon(BigInteger JavaDoc 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     */

1235    public void setMonth(int month) {
1236    checkFieldValueConstraint(MONTH, month);
1237    this.month = month;
1238    }
1239
1240    /**
1241     * <p>Set days in month.</p>
1242     *
1243     * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1244     *
1245     * @param day value constraints summarized in <a HREF="#datetimefield-day">day field of date/time field mapping table</a>.
1246     *
1247     * @throws IllegalArgumentException if <code>day</code> parameter is
1248     * outside value constraints for the field as specified in
1249     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1250     */

1251    public void setDay(int day) {
1252    checkFieldValueConstraint(DAY, day);
1253    this.day = day;
1254    }
1255
1256    /**
1257     * <p>Set the number of minutes in the timezone offset.</p>
1258     *
1259     * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1260     *
1261     * @param offset value constraints summarized in <a HREF="#datetimefield-timezone">
1262     * timezone field of date/time field mapping table</a>.
1263     *
1264     * @throws IllegalArgumentException if <code>offset</code> parameter is
1265     * outside value constraints for the field as specified in
1266     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1267     */

1268    public void setTimezone(int offset) {
1269    checkFieldValueConstraint(TIMEZONE, offset);
1270    this.timezone = offset;
1271    }
1272
1273    /**
1274     * <p>Set time as one unit.</p>
1275     *
1276     * @param hour value constraints are summarized in
1277     * <a HREF="#datetimefield-hour">hour field of date/time field mapping table</a>.
1278     * @param minute value constraints are summarized in
1279     * <a HREF="#datetimefield-minute">minute field of date/time field mapping table</a>.
1280     * @param second value constraints are summarized in
1281     * <a HREF="#datetimefield-second">second field of date/time field mapping table</a>.
1282     *
1283     * @see #setTime(int, int, int, BigDecimal)
1284     *
1285     * @throws IllegalArgumentException if any parameter is
1286     * outside value constraints for the field as specified in
1287     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1288     */

1289    public void setTime(int hour, int minute, int second) {
1290    setTime(hour, minute, second, null);
1291    }
1292
1293    private void checkFieldValueConstraint(int field, int value)
1294    throws IllegalArgumentException JavaDoc
1295    {
1296    if ((value < MIN_FIELD_VALUE[field] && value != DatatypeConstants.FIELD_UNDEFINED) ||
1297        value > MAX_FIELD_VALUE[field]) {
1298         /**
1299            throw new IllegalArgumentException("invalid value " + value +
1300                           " for " + FIELD_NAME[field] +
1301                           " field");
1302             */

1303        throw new IllegalArgumentException JavaDoc(
1304            DatatypeMessageFormatter.formatMessage(null, "InvalidFieldValue", new Object JavaDoc[]{ new Integer JavaDoc(value), FIELD_NAME[field]})
1305        );
1306    }
1307    }
1308
1309    public void setHour(int hour) {
1310    checkFieldValueConstraint(HOUR, hour);
1311    this.hour = hour;
1312    }
1313
1314    public void setMinute(int minute) {
1315    checkFieldValueConstraint(MINUTE, minute);
1316    this.minute = minute;
1317    }
1318
1319    public void setSecond(int second) {
1320    checkFieldValueConstraint(SECOND, second);
1321    this.second = second;
1322    }
1323
1324    /**
1325     * <p>Set time as one unit, including the optional infinite precison
1326     * fractional seconds.</p>
1327     *
1328     * @param hour value constraints are summarized in
1329     * <a HREF="#datetimefield-hour">hour field of date/time field mapping table</a>.
1330     * @param minute value constraints are summarized in
1331     * <a HREF="#datetimefield-minute">minute field of date/time field mapping table</a>.
1332     * @param second value constraints are summarized in
1333     * <a HREF="#datetimefield-second">second field of date/time field mapping table</a>.
1334     * @param fractional value of <code>null</code> indicates this optional
1335     * field is not set.
1336     *
1337     * @throws IllegalArgumentException if any parameter is
1338     * outside value constraints for the field as specified in
1339     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1340     */

1341    public void setTime(
1342        int hour,
1343        int minute,
1344        int second,
1345        BigDecimal JavaDoc fractional) {
1346    setHour(hour);
1347        setMinute(minute);
1348        setSecond(second);
1349        setFractionalSecond(fractional);
1350    }
1351    
1352
1353    /**
1354     * <p>Set time as one unit, including optional milliseconds.</p>
1355     *
1356     * @param hour value constraints are summarized in
1357     * <a HREF="#datetimefield-hour">hour field of date/time field mapping table</a>.
1358     * @param minute value constraints are summarized in
1359     * <a HREF="#datetimefield-minute">minute field of date/time field mapping table</a>.
1360     * @param second value constraints are summarized in
1361     * <a HREF="#datetimefield-second">second field of date/time field mapping table</a>.
1362     * @param millisecond value of {@link DatatypeConstants#FIELD_UNDEFINED} indicates this
1363     * optional field is not set.
1364     *
1365     * @throws IllegalArgumentException if any parameter is
1366     * outside value constraints for the field as specified in
1367     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.
1368     */

1369    public void setTime(int hour, int minute, int second, int millisecond) {
1370        setHour(hour);
1371        setMinute(minute);
1372        setSecond(second);
1373        setMillisecond(millisecond);
1374    }
1375
1376    // comparisons
1377
/**
1378     * <p>Compare two instances of W3C XML Schema 1.0 date/time datatypes
1379     * according to partial order relation defined in
1380     * <a HREF="http://www.w3.org/TR/xmlschema-2/#dateTime-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.3,
1381     * <i>Order relation on dateTime</i></a>.</p>
1382     *
1383     * <p><code>xsd:dateTime</code> datatype field mapping to accessors of
1384     * this class are defined in
1385     * <a HREF="#datetimefieldmapping">date/time field mapping table</a>.</p>
1386     *
1387     * @param rhs instance of <code>XMLGregorianCalendar</code> to compare
1388     *
1389     * @return the relationship between <code>lhs</code> and <code>rhs</code> as
1390     * {@link DatatypeConstants#LESSER},
1391     * {@link DatatypeConstants#EQUAL},
1392     * {@link DatatypeConstants#GREATER} or
1393     * {@link DatatypeConstants#INDETERMINATE}.
1394     *
1395     * @throws NullPointerException if <code>lhs</code> or <code>rhs</code>
1396     * parameters are null.
1397     */

1398    public int compare(XMLGregorianCalendar JavaDoc rhs) {
1399            
1400        XMLGregorianCalendar JavaDoc lhs = this;
1401
1402    int result = DatatypeConstants.INDETERMINATE;
1403        XMLGregorianCalendarImpl P = (XMLGregorianCalendarImpl) lhs;
1404        XMLGregorianCalendarImpl Q = (XMLGregorianCalendarImpl) rhs;
1405
1406        if (P.getTimezone() == Q.getTimezone()) {
1407        // Optimization:
1408
// both instances are in same timezone or
1409
// both are FIELD_UNDEFINED.
1410
// Avoid costly normalization of timezone to 'Z' time.
1411
return internalCompare(P, Q);
1412
1413    } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED &&
1414           Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1415
1416        // Both instances have different timezones.
1417
// Normalize to UTC time and compare.
1418
P = (XMLGregorianCalendarImpl) P.normalize();
1419        Q = (XMLGregorianCalendarImpl) Q.normalize();
1420        return internalCompare(P, Q);
1421    } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1422        
1423        if (P.getTimezone() != 0) {
1424        P = (XMLGregorianCalendarImpl) P.normalize();
1425        }
1426        
1427        // C. step 1
1428
XMLGregorianCalendar JavaDoc MinQ = Q.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1429        result = internalCompare(P, MinQ);
1430        if (result == DatatypeConstants.LESSER) {
1431        return result;
1432        }
1433
1434        // C. step 2
1435
XMLGregorianCalendar JavaDoc MaxQ = Q.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1436        result = internalCompare(P, MaxQ);
1437        if (result == DatatypeConstants.GREATER) {
1438        return result;
1439        } else {
1440        // C. step 3
1441
return DatatypeConstants.INDETERMINATE;
1442        }
1443    } else { // Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
1444
// P has no timezone and Q does.
1445
if (Q.getTimezone() != 0 ) {
1446            Q = (XMLGregorianCalendarImpl) Q.normalizeToTimezone(Q.getTimezone());
1447        }
1448        
1449        // D. step 1
1450
XMLGregorianCalendar JavaDoc MaxP = P.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1451        result = internalCompare(MaxP, Q);
1452        if (result == DatatypeConstants.LESSER) {
1453        return result;
1454        }
1455
1456        // D. step 2
1457
XMLGregorianCalendar JavaDoc MinP = P.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1458        result = internalCompare(MinP, Q);
1459        if (result == DatatypeConstants.GREATER) {
1460        return result;
1461        } else {
1462        // D. step 3
1463
return DatatypeConstants.INDETERMINATE;
1464        }
1465        }
1466    }
1467
1468    /**
1469     * <p>Normalize this instance to UTC.</p>
1470     *
1471     * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1472     * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1473     */

1474    public XMLGregorianCalendar JavaDoc normalize() {
1475
1476        XMLGregorianCalendar JavaDoc normalized = normalizeToTimezone(timezone);
1477        
1478        // if timezone was undefined, leave it undefined
1479
if (getTimezone() == DatatypeConstants.FIELD_UNDEFINED) {
1480            normalized.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
1481        }
1482        
1483        // if milliseconds was undefined, leave it undefined
1484
if (getMillisecond() == DatatypeConstants.FIELD_UNDEFINED) {
1485            normalized.setMillisecond(DatatypeConstants.FIELD_UNDEFINED);
1486        }
1487        
1488        return normalized;
1489    }
1490    
1491    /**
1492     * <p>Normalize this instance to UTC.</p>
1493     *
1494     * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1495     * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1496     */

1497    private XMLGregorianCalendar JavaDoc normalizeToTimezone(int timezone) {
1498
1499    int minutes = timezone;
1500    XMLGregorianCalendar JavaDoc result = (XMLGregorianCalendar JavaDoc) this.clone();
1501    
1502    // normalizing to UTC time negates the timezone offset before
1503
// addition.
1504
minutes = -minutes;
1505    Duration JavaDoc d = new DurationImpl(minutes >= 0, // isPositive
1506
0, //years
1507
0, //months
1508
0, //days
1509
0, //hours
1510
minutes < 0 ? -minutes : minutes, // absolute
1511
0 //seconds
1512
);
1513    result.add(d);
1514
1515    // set to zulu UTC time.
1516
result.setTimezone(0);
1517    return result;
1518    }
1519
1520    /**
1521     *
1522     * <p>Implements Step B from http://www.w3.org/TR/xmlschema-2/#dateTime-order </p>
1523     * @param P calendar instance with normalized timezone offset or
1524     * having same timezone as Q
1525     * @param Q calendar instance with normalized timezone offset or
1526     * having same timezone as P
1527     *
1528     * @return result of comparing P and Q, value of
1529     * {@link DatatypeConstants#EQUAL},
1530     * {@link DatatypeConstants#LESSER},
1531     * {@link DatatypeConstants#GREATER} or
1532     * {@link DatatypeConstants#INDETERMINATE}.
1533     */

1534    private static int internalCompare(XMLGregorianCalendar JavaDoc P,
1535                       XMLGregorianCalendar JavaDoc Q) {
1536
1537    int result;
1538
1539    // compare Year.
1540
if (P.getEon() == Q.getEon()) {
1541        
1542        // Eon field is only equal when null.
1543
// optimized case for comparing year not requiring eon field.
1544
result = compareField(P.getYear(), Q.getYear());
1545        if (result != DatatypeConstants.EQUAL) {
1546        return result;
1547        }
1548    } else {
1549        result = compareField(P.getEonAndYear(), Q.getEonAndYear());
1550        if (result != DatatypeConstants.EQUAL) {
1551        return result;
1552        }
1553    }
1554
1555    result = compareField(P.getMonth(), Q.getMonth());
1556    if (result != DatatypeConstants.EQUAL) {
1557        return result;
1558    }
1559
1560    result = compareField(P.getDay(), Q.getDay());
1561    if (result != DatatypeConstants.EQUAL) {
1562        return result;
1563    }
1564
1565    result = compareField(P.getHour(), Q.getHour());
1566    if (result != DatatypeConstants.EQUAL) {
1567        return result;
1568    }
1569    
1570    result = compareField(P.getMinute(), Q.getMinute());
1571    if (result != DatatypeConstants.EQUAL) {
1572        return result;
1573    }
1574    result = compareField(P.getSecond(), Q.getSecond());
1575    if (result != DatatypeConstants.EQUAL) {
1576        return result;
1577    }
1578    
1579    result = compareField(P.getFractionalSecond(), Q.getFractionalSecond());
1580    return result;
1581   }
1582
1583    /**
1584     * <p>Implement Step B from
1585     * http://www.w3.org/TR/xmlschema-2/#dateTime-order.</p>
1586     */

1587    private static int compareField(int Pfield, int Qfield) {
1588    if (Pfield == Qfield) {
1589
1590        //fields are either equal in value or both undefined.
1591
// Step B. 1.1 AND optimized result of performing 1.1-1.4.
1592
return DatatypeConstants.EQUAL;
1593    } else {
1594        if (Pfield == DatatypeConstants.FIELD_UNDEFINED || Qfield == DatatypeConstants.FIELD_UNDEFINED) {
1595        // Step B. 1.2
1596
return DatatypeConstants.INDETERMINATE;
1597        } else {
1598        // Step B. 1.3-4.
1599
return (Pfield < Qfield ? DatatypeConstants.LESSER : DatatypeConstants.GREATER);
1600        }
1601    }
1602    }
1603
1604    private static int compareField(BigInteger JavaDoc Pfield, BigInteger JavaDoc Qfield) {
1605    if (Pfield == null) {
1606        return (Qfield == null ? DatatypeConstants.EQUAL : DatatypeConstants.INDETERMINATE);
1607    }
1608    if (Qfield == null) {
1609        return DatatypeConstants.INDETERMINATE;
1610    }
1611    return Pfield.compareTo(Qfield);
1612    }
1613
1614    private static int compareField(BigDecimal JavaDoc Pfield, BigDecimal JavaDoc Qfield) {
1615    // optimization. especially when both arguments are null.
1616
if (Pfield == Qfield) {
1617        return DatatypeConstants.EQUAL;
1618    }
1619
1620    if (Pfield == null) {
1621        Pfield = DECIMAL_ZERO;
1622    }
1623
1624    if (Qfield == null) {
1625        Qfield = DECIMAL_ZERO;
1626    }
1627    
1628    return Pfield.compareTo(Qfield);
1629    }
1630
1631    /**
1632     * <p>Indicates whether parameter <code>obj</code> is "equal to" this one.</p>
1633     *
1634     * @param obj to compare.
1635     *
1636     * @return <code>true</code> when <code>compare(this,(XMLGregorianCalendar)obj) == EQUAL.</code>.
1637     */

1638    public boolean equals(Object JavaDoc obj) {
1639       boolean result = false;
1640       if (obj instanceof XMLGregorianCalendar JavaDoc) {
1641           result = compare((XMLGregorianCalendar JavaDoc) obj) == DatatypeConstants.EQUAL;
1642       }
1643       return result;
1644    }
1645
1646    /**
1647     * <p>Returns a hash code consistent with the definition of the equals method.</p>
1648     *
1649     * @return hash code of this object.
1650     */

1651    public int hashCode() {
1652
1653    // Following two dates compare to EQUALS since in different timezones.
1654
// 2000-01-15T12:00:00-05:00 == 2000-01-15T13:00:00-04:00
1655
//
1656
// Must ensure both instances generate same hashcode by normalizing
1657
// this to UTC timezone.
1658
int timezone = getTimezone();
1659    if (timezone == DatatypeConstants.FIELD_UNDEFINED){
1660        timezone = 0;
1661    }
1662    XMLGregorianCalendar JavaDoc gc = this;
1663    if (timezone != 0) {
1664        gc = this.normalizeToTimezone(getTimezone());
1665    }
1666    return gc.getYear() + gc.getMonth() + gc.getDay() +
1667        gc.getHour() + gc.getMinute() + gc.getSecond();
1668    }
1669    
1670    
1671    /**
1672     * <p>Constructs a new XMLGregorianCalendar object by
1673     * parsing its lexical string representation as defined in
1674     * <a HREF="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1675     * <i>Lexical Representation</i>.</a></p>
1676     *
1677     * <p>The string representation may not have any leading and trailing whitespaces.</p>
1678     *
1679     * <p>The parsing is done field by field so that
1680     * the following holds for any lexically correct string x:</p>
1681     * <pre>
1682     * new XMLGregorianCalendar(x).toXMLFormat().equals(x)
1683     * </pre>
1684     * Except for the noted lexical/canonical representation mismatches
1685     * listed in <a HREF="http://www.w3.org/2001/05/xmlschema-errata#e2-45">
1686     * XML Schema 1.0 errata, Section 3.2.7.2</a>.
1687     *
1688     * <p>Returns a non-null valid XMLGregorianCalendar object that holds the value
1689     * indicated by the lexicalRepresentation parameter.</p>
1690     *
1691     * @param lexicalRepresentation Lexical representation of one the 8 XML Schema calendar datatypes.
1692     *
1693     * @return <code>XMLGregorianCalendar</code> created from parsing <code>lexicalRepresentation</code> parameter.
1694     *
1695     * @throws IllegalArgumentException
1696     * If the given string does not conform to the aforementioned
1697     * specification.
1698     * @throws NullPointerException
1699     * If the given string is null.
1700     */

1701    public static XMLGregorianCalendar JavaDoc parse(String JavaDoc lexicalRepresentation) {
1702        
1703        return new XMLGregorianCalendarImpl(lexicalRepresentation);
1704    }
1705
1706    /**
1707     * <p>Return the lexical representation of <code>this</code> instance.
1708     * The format is specified in
1709     * <a HREF="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1710     * <i>Lexical Representation</i>".</a></p>
1711     *
1712     * <p>Specific target lexical representation format is determined by
1713     * {@link #getXMLSchemaType()}.</p>
1714     *
1715     * @return XML, as <code>String</code>, representation of this <code>XMLGregorianCalendar</code>
1716     *
1717     * @throws java.lang.IllegalStateException if the combination of set fields
1718     * does not match one of the eight defined XML Schema builtin date/time datatypes.
1719     */

1720    public String JavaDoc toXMLFormat() {
1721        
1722    QName JavaDoc typekind = getXMLSchemaType();
1723    
1724    String JavaDoc formatString = null;
1725    if (typekind == DatatypeConstants.DATETIME) {
1726        formatString = "%Y-%M-%DT%h:%m:%s"+ "%z";
1727    } else if (typekind == DatatypeConstants.DATE) {
1728        // Fix 4971612: invalid SCCS macro substitution in data string
1729
formatString = "%Y-%M-%D" +"%z";
1730    } else if (typekind == DatatypeConstants.TIME) {
1731        formatString = "%h:%m:%s"+ "%z";
1732    } else if (typekind == DatatypeConstants.GMONTH) {
1733        formatString = "--%M--%z";
1734    } else if (typekind == DatatypeConstants.GDAY) {
1735        // Fix 4971612: invalid SCCS macro substitution in data string
1736
formatString = "---%D" + "%z";
1737    } else if (typekind == DatatypeConstants.GYEAR) {
1738        formatString = "%Y" + "%z";
1739    } else if (typekind == DatatypeConstants.GYEARMONTH) {
1740        // Fix 4971612: invalid SCCS macro substitution in data string
1741
formatString = "%Y-%M" + "%z";
1742    } else if (typekind == DatatypeConstants.GMONTHDAY) {
1743        // Fix 4971612: invalid SCCS macro substitution in data string
1744
formatString = "--%M-%D" +"%z";
1745    }
1746    return format(formatString);
1747    }
1748    
1749    /**
1750     * <p>Return the name of the XML Schema date/time type that this instance
1751     * maps to. Type is computed based on fields that are set.</p>
1752     *
1753     * <table border="2" rules="all" cellpadding="2">
1754     * <thead>
1755     * <tr>
1756     * <th align="center" colspan="7">
1757     * Required fields for XML Schema 1.0 Date/Time Datatypes.<br/>
1758     * <i>(timezone is optional for all date/time datatypes)</i>
1759     * </th>
1760     * </tr>
1761     * </thead>
1762     * <tbody>
1763     * <tr>
1764     * <td>Datatype</td>
1765     * <td>year</td>
1766     * <td>month</td>
1767     * <td>day</td>
1768     * <td>hour</td>
1769     * <td>minute</td>
1770     * <td>second</td>
1771     * </tr>
1772     * <tr>
1773     * <td>{@link DatatypeConstants#DATETIME}</td>
1774     * <td>X</td>
1775     * <td>X</td>
1776     * <td>X</td>
1777     * <td>X</td>
1778     * <td>X</td>
1779     * <td>X</td>
1780     * </tr>
1781     * <tr>
1782     * <td>{@link DatatypeConstants#DATE}</td>
1783     * <td>X</td>
1784     * <td>X</td>
1785     * <td>X</td>
1786     * <td></td>
1787     * <td></td>
1788     * <td></td>
1789     * </tr>
1790     * <tr>
1791     * <td>{@link DatatypeConstants#TIME}</td>
1792     * <td></td>
1793     * <td></td>
1794     * <td></td>
1795     * <td>X</td>
1796     * <td>X</td>
1797     * <td>X</td>
1798     * </tr>
1799     * <tr>
1800     * <td>{@link DatatypeConstants#GYEARMONTH}</td>
1801     * <td>X</td>
1802     * <td>X</td>
1803     * <td></td>
1804     * <td></td>
1805     * <td></td>
1806     * <td></td>
1807     * </tr>
1808     * <tr>
1809     * <td>{@link DatatypeConstants#GMONTHDAY}</td>
1810     * <td></td>
1811     * <td>X</td>
1812     * <td>X</td>
1813     * <td></td>
1814     * <td></td>
1815     * <td></td>
1816     * </tr>
1817     * <tr>
1818     * <td>{@link DatatypeConstants#GYEAR}</td>
1819     * <td>X</td>
1820     * <td></td>
1821     * <td></td>
1822     * <td></td>
1823     * <td></td>
1824     * <td></td>
1825     * </tr>
1826     * <tr>
1827     * <td>{@link DatatypeConstants#GMONTH}</td>
1828     * <td></td>
1829     * <td>X</td>
1830     * <td></td>
1831     * <td></td>
1832     * <td></td>
1833     * <td></td>
1834     * </tr>
1835     * <tr>
1836     * <td>{@link DatatypeConstants#GDAY}</td>
1837     * <td></td>
1838     * <td></td>
1839     * <td>X</td>
1840     * <td></td>
1841     * <td></td>
1842     * <td></td>
1843     * </tr>
1844     * </tbody>
1845     * </table>
1846     *
1847     * @throws java.lang.IllegalStateException if the combination of set fields
1848     * does not match one of the eight defined XML Schema builtin
1849     * date/time datatypes.
1850     * @return One of the following class constants:
1851     * {@link DatatypeConstants#DATETIME},
1852     * {@link DatatypeConstants#TIME},
1853     * {@link DatatypeConstants#DATE},
1854     * {@link DatatypeConstants#GYEARMONTH},
1855     * {@link DatatypeConstants#GMONTHDAY},
1856     * {@link DatatypeConstants#GYEAR},
1857     * {@link DatatypeConstants#GMONTH} or
1858     * {@link DatatypeConstants#GDAY}.
1859     */

1860    public QName JavaDoc getXMLSchemaType() {
1861        
1862        // DATETIME
1863
if (year != DatatypeConstants.FIELD_UNDEFINED
1864            && month != DatatypeConstants.FIELD_UNDEFINED
1865            && day != DatatypeConstants.FIELD_UNDEFINED
1866            && hour != DatatypeConstants.FIELD_UNDEFINED
1867            && minute != DatatypeConstants.FIELD_UNDEFINED
1868            && second != DatatypeConstants.FIELD_UNDEFINED) {
1869            return DatatypeConstants.DATETIME;
1870        }
1871
1872        // DATE
1873
if (year != DatatypeConstants.FIELD_UNDEFINED
1874            && month != DatatypeConstants.FIELD_UNDEFINED
1875            && day != DatatypeConstants.FIELD_UNDEFINED
1876            && hour == DatatypeConstants.FIELD_UNDEFINED
1877            && minute == DatatypeConstants.FIELD_UNDEFINED
1878            && second == DatatypeConstants.FIELD_UNDEFINED) {
1879            return DatatypeConstants.DATE;
1880        }
1881
1882        // TIME
1883
if (year == DatatypeConstants.FIELD_UNDEFINED
1884            && month == DatatypeConstants.FIELD_UNDEFINED
1885            && day == DatatypeConstants.FIELD_UNDEFINED
1886            && hour != DatatypeConstants.FIELD_UNDEFINED
1887            && minute != DatatypeConstants.FIELD_UNDEFINED
1888            && second != DatatypeConstants.FIELD_UNDEFINED) {
1889            return DatatypeConstants.TIME;
1890        }
1891
1892
1893        // GYEARMONTH
1894
if (year != DatatypeConstants.FIELD_UNDEFINED
1895            && month != DatatypeConstants.FIELD_UNDEFINED
1896            && day == DatatypeConstants.FIELD_UNDEFINED
1897            && hour == DatatypeConstants.FIELD_UNDEFINED
1898            && minute == DatatypeConstants.FIELD_UNDEFINED
1899            && second == DatatypeConstants.FIELD_UNDEFINED) {
1900            return DatatypeConstants.GYEARMONTH;
1901        }
1902
1903        // GMONTHDAY
1904
if (year == DatatypeConstants.FIELD_UNDEFINED
1905            && month != DatatypeConstants.FIELD_UNDEFINED
1906            && day != DatatypeConstants.FIELD_UNDEFINED
1907            && hour == DatatypeConstants.FIELD_UNDEFINED
1908            && minute == DatatypeConstants.FIELD_UNDEFINED
1909            && second == DatatypeConstants.FIELD_UNDEFINED) {
1910            return DatatypeConstants.GMONTHDAY;
1911        }
1912
1913        // GYEAR
1914
if (year != DatatypeConstants.FIELD_UNDEFINED
1915            && month == DatatypeConstants.FIELD_UNDEFINED
1916            && day == DatatypeConstants.FIELD_UNDEFINED
1917            && hour == DatatypeConstants.FIELD_UNDEFINED
1918            && minute == DatatypeConstants.FIELD_UNDEFINED
1919            && second == DatatypeConstants.FIELD_UNDEFINED) {
1920            return DatatypeConstants.GYEAR;
1921        }
1922
1923        // GMONTH
1924
if (year == DatatypeConstants.FIELD_UNDEFINED
1925            && month != DatatypeConstants.FIELD_UNDEFINED
1926            && day == DatatypeConstants.FIELD_UNDEFINED
1927            && hour == DatatypeConstants.FIELD_UNDEFINED
1928            && minute == DatatypeConstants.FIELD_UNDEFINED
1929            && second == DatatypeConstants.FIELD_UNDEFINED) {
1930            return DatatypeConstants.GMONTH;
1931        }
1932
1933        // GDAY
1934
if (year == DatatypeConstants.FIELD_UNDEFINED
1935            && month == DatatypeConstants.FIELD_UNDEFINED
1936            && day != DatatypeConstants.FIELD_UNDEFINED
1937            && hour == DatatypeConstants.FIELD_UNDEFINED
1938            && minute == DatatypeConstants.FIELD_UNDEFINED
1939            && second == DatatypeConstants.FIELD_UNDEFINED) {
1940            return DatatypeConstants.GDAY;
1941        }
1942
1943        // unknown
1944
throw new IllegalStateException JavaDoc(
1945            this.getClass().getName()
1946            + "#getXMLSchemaType() :"
1947            + DatatypeMessageFormatter.formatMessage(null, "InvalidXGCFields", null)
1948        );
1949    }
1950
1951    
1952    /**
1953     * Validate instance by <code>getXMLSchemaType()</code> constraints.
1954     * @return true if data values are valid.
1955     */

1956    public 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 (getMonth() == DatatypeConstants.FEBRUARY) {
1964        // years could not be set
1965
int maxDays = DatatypeConstants.FIELD_UNDEFINED;
1966        BigInteger JavaDoc years = getEonAndYear();
1967        if (years != null) {
1968            maxDays = maximumDayInMonthFor(getEonAndYear(), DatatypeConstants.FEBRUARY);
1969        } else {
1970            // year is undefined, allow 29 days
1971
maxDays = 29;
1972        }
1973        if (getDay() > maxDays) {
1974        return false;
1975        }
1976    }
1977
1978    // http://www.w3.org/2001/05/xmlschema-errata#e2-45
1979
if (getHour() == 24) {
1980        if(getMinute() != 0) {
1981        return false;
1982        } else if (getSecond() != 0) {
1983        return false;
1984        }
1985    }
1986
1987    // XML Schema 1.0 specification defines year value of zero as
1988
// invalid. Allow this class to set year field to zero
1989
// since XML Schema 1.0 errata states that lexical zero will
1990
// be allowed in next version and treated as 1 B.C.E.
1991
if (eon == null) {
1992        // optimize check.
1993
if (year == 0) {
1994        return false;
1995        }
1996    } else {
1997        BigInteger JavaDoc yearField = getEonAndYear();
1998        if (yearField != null) {
1999        int result = compareField(yearField, BigInteger.ZERO);
2000        if (result == DatatypeConstants.EQUAL) {
2001            return false;
2002        }
2003        }
2004    }
2005    return true;
2006    }
2007
2008    /**
2009     * <p>Add <code>duration</code> to this instance.<\p>
2010     *
2011     * <p>The computation is specified in
2012     * <a HREF="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
2013     * <i>Adding durations to dateTimes</i>></a>.
2014     * <a HREF="#datetimefieldsmapping">date/time field mapping table</a>
2015     * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
2016     * to this class' representation of those fields.</p>
2017     *
2018     * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
2019     *
2020     * @throws NullPointerException when <code>duration</code> parameter is <code>null</code>.
2021     */

2022    public void add(Duration JavaDoc duration) {
2023        
2024        /*
2025         * Extracted from
2026         * http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
2027         * to ensure implemented properly. See spec for definitions of methods
2028         * used in algorithm.
2029         *
2030         * Given a dateTime S and a duration D, specifies how to compute a
2031         * dateTime E where E is the end of the time period with start S and
2032         * duration D i.e. E = S + D.
2033         *
2034         * The following is the precise specification.
2035         * These steps must be followed in the same order.
2036         * If a field in D is not specified, it is treated as if it were zero.
2037         * If a field in S is not specified, it is treated in the calculation
2038         * as if it were the minimum allowed value in that field, however,
2039         * after the calculation is concluded, the corresponding field in
2040         * E is removed (set to unspecified).
2041         *
2042         * Months (may be modified additionally below)
2043             * temp := S[month] + D[month]
2044             * E[month] := modulo(temp, 1, 13)
2045             * carry := fQuotient(temp, 1, 13)
2046         */

2047     
2048        boolean fieldUndefined[] = {
2049            false,
2050            false,
2051            false,
2052            false,
2053            false,
2054            false
2055        };
2056        
2057        int signum = duration.getSign();
2058        
2059        int startMonth = getMonth();
2060        if (startMonth == DatatypeConstants.FIELD_UNDEFINED) {
2061            startMonth = MIN_FIELD_VALUE[MONTH];
2062            fieldUndefined[MONTH] = true;
2063        }
2064        
2065        BigInteger JavaDoc dMonths = sanitize(duration.getField(DatatypeConstants.MONTHS), signum);
2066        BigInteger JavaDoc temp = BigInteger.valueOf((long) startMonth).add(dMonths);
2067        setMonth(temp.subtract(BigInteger.ONE).mod(TWELVE).intValue() + 1);
2068        BigInteger JavaDoc carry =
2069            new BigDecimal JavaDoc(temp.subtract(BigInteger.ONE)).divide(new BigDecimal JavaDoc(TWELVE), BigDecimal.ROUND_FLOOR).toBigInteger();
2070
2071         /* Years (may be modified additionally below)
2072          * E[year] := S[year] + D[year] + carry
2073          */

2074        BigInteger JavaDoc startYear = getEonAndYear();
2075        if (startYear == null) {
2076            fieldUndefined[YEAR] = true;
2077            startYear = BigInteger.ZERO;
2078        }
2079        BigInteger JavaDoc dYears = sanitize(duration.getField(DatatypeConstants.YEARS), signum);
2080        BigInteger JavaDoc endYear = startYear.add(dYears).add(carry);
2081        setYear(endYear);
2082
2083        /* Zone
2084             * E[zone] := S[zone]
2085         *
2086         * no-op since adding to this, not to a new end point.
2087         */

2088    
2089         /* Seconds
2090          * temp := S[second] + D[second]
2091          * E[second] := modulo(temp, 60)
2092          * carry := fQuotient(temp, 60)
2093          */

2094        BigDecimal JavaDoc startSeconds;
2095        if (getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
2096            fieldUndefined[SECOND] = true;
2097            startSeconds = DECIMAL_ZERO;
2098        } else {
2099            // seconds + fractionalSeconds
2100
startSeconds = getSeconds();
2101        }
2102
2103        // Duration seconds is SECONDS + FRACTIONALSECONDS.
2104
BigDecimal JavaDoc dSeconds = DurationImpl.sanitize((BigDecimal JavaDoc) duration.getField(DatatypeConstants.SECONDS), signum);
2105        BigDecimal JavaDoc tempBD = startSeconds.add(dSeconds);
2106        BigDecimal JavaDoc fQuotient =
2107            new BigDecimal JavaDoc(new BigDecimal JavaDoc(tempBD.toBigInteger()).divide(DECIMAL_SIXTY, BigDecimal.ROUND_FLOOR).toBigInteger());
2108        BigDecimal JavaDoc endSeconds = tempBD.subtract(fQuotient.multiply(DECIMAL_SIXTY));
2109
2110        carry = fQuotient.toBigInteger();
2111        setSecond(endSeconds.intValue());
2112        BigDecimal JavaDoc tempFracSeconds = endSeconds.subtract(new BigDecimal JavaDoc(BigInteger.valueOf((long) getSecond())));
2113        if (tempFracSeconds.compareTo(DECIMAL_ZERO) < 0) {
2114            setFractionalSecond(DECIMAL_ONE.add(tempFracSeconds));
2115            if (getSecond() == 0) {
2116                setSecond(59);
2117                carry = carry.subtract(BigInteger.ONE);
2118            } else {
2119                setSecond(getSecond() - 1);
2120            }
2121        } else {
2122            setFractionalSecond(tempFracSeconds);
2123        }
2124 
2125        /* Minutes
2126             * temp := S[minute] + D[minute] + carry
2127             * E[minute] := modulo(temp, 60)
2128             * carry := fQuotient(temp, 60)
2129         */

2130        int startMinutes = getMinute();
2131        if (startMinutes == DatatypeConstants.FIELD_UNDEFINED) {
2132            fieldUndefined[MINUTE] = true;
2133            startMinutes = MIN_FIELD_VALUE[MINUTE];
2134        }
2135        BigInteger JavaDoc dMinutes = sanitize(duration.getField(DatatypeConstants.MINUTES), signum);
2136        
2137        temp = BigInteger.valueOf(startMinutes).add(dMinutes).add(carry);
2138        setMinute(temp.mod(SIXTY).intValue());
2139        carry = new BigDecimal JavaDoc(temp).divide(DECIMAL_SIXTY, BigDecimal.ROUND_FLOOR).toBigInteger();
2140
2141        /* Hours
2142             * temp := S[hour] + D[hour] + carry
2143             * E[hour] := modulo(temp, 24)
2144             * carry := fQuotient(temp, 24)
2145         */

2146        int startHours = getHour();
2147        if (startHours == DatatypeConstants.FIELD_UNDEFINED) {
2148            fieldUndefined[HOUR] = true;
2149            startHours = MIN_FIELD_VALUE[HOUR];
2150        }
2151        BigInteger JavaDoc dHours = sanitize(duration.getField(DatatypeConstants.HOURS), signum);
2152        
2153        temp = BigInteger.valueOf(startHours).add(dHours).add(carry);
2154        setHour(temp.mod(TWENTY_FOUR).intValue());
2155        carry = new BigDecimal JavaDoc(temp).divide(new BigDecimal JavaDoc(TWENTY_FOUR), BigDecimal.ROUND_FLOOR).toBigInteger();
2156    
2157        /* Days
2158         * if S[day] > maximumDayInMonthFor(E[year], E[month])
2159         * + tempDays := maximumDayInMonthFor(E[year], E[month])
2160         * else if S[day] < 1
2161         * + tempDays := 1
2162         * else
2163         * + tempDays := S[day]
2164         * E[day] := tempDays + D[day] + carry
2165         * START LOOP
2166         * + IF E[day] < 1
2167         * # E[day] := E[day] +
2168         * maximumDayInMonthFor(E[year], E[month] - 1)
2169         * # carry := -1
2170         * + ELSE IF E[day] > maximumDayInMonthFor(E[year], E[month])
2171         * # E[day] :=
2172         * E[day] - maximumDayInMonthFor(E[year], E[month])
2173         * # carry := 1
2174         * + ELSE EXIT LOOP
2175         * + temp := E[month] + carry
2176         * + E[month] := modulo(temp, 1, 13)
2177         * + E[year] := E[year] + fQuotient(temp, 1, 13)
2178         * + GOTO START LOOP
2179         */

2180        BigInteger JavaDoc tempDays;
2181        int startDay = getDay();
2182        if (startDay == DatatypeConstants.FIELD_UNDEFINED) {
2183            fieldUndefined[DAY] = true;
2184            startDay = MIN_FIELD_VALUE[DAY];
2185        }
2186        BigInteger JavaDoc dDays = sanitize(duration.getField(DatatypeConstants.DAYS), signum);
2187        int maxDayInMonth = maximumDayInMonthFor(getEonAndYear(), getMonth());
2188        if (startDay > maxDayInMonth) {
2189                tempDays = BigInteger.valueOf(maxDayInMonth);
2190        } else if (startDay < 1) {
2191            tempDays = BigInteger.ONE;
2192        } else {
2193            tempDays = BigInteger.valueOf(startDay);
2194        }
2195        BigInteger JavaDoc endDays = tempDays.add(dDays).add(carry);
2196        int monthCarry;
2197        int intTemp;
2198        while (true) {
2199            if (endDays.compareTo(BigInteger.ONE) < 0) {
2200                // calculate days in previous month, watch for month roll over
2201
BigInteger JavaDoc mdimf = null;
2202                if (month >= 2) {
2203                    mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth() - 1));
2204                } else {
2205                    // roll over to December of previous year
2206
mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear().subtract(BigInteger.valueOf((long) 1)), 12));
2207                }
2208                endDays = endDays.add(mdimf);
2209                monthCarry = -1;
2210            } else if (endDays.compareTo(BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
2211                endDays = endDays.add(BigInteger.valueOf(-maximumDayInMonthFor(getEonAndYear(), getMonth())));
2212                monthCarry = 1;
2213            } else {
2214                break;
2215            }
2216
2217            intTemp = getMonth() + monthCarry;
2218            int endMonth = (intTemp - 1) % (13 - 1);
2219            int quotient;
2220            if (endMonth < 0) {
2221                endMonth = (13 - 1) + endMonth + 1;
2222                quotient = new BigDecimal JavaDoc(intTemp - 1).divide(new BigDecimal JavaDoc(TWELVE), BigDecimal.ROUND_UP).intValue();
2223            } else {
2224                quotient = (intTemp - 1) / (13 - 1);
2225                endMonth += 1;
2226            }
2227            setMonth(endMonth);
2228            if (quotient != 0) {
2229                setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
2230            }
2231        }
2232        setDay(endDays.intValue());
2233    
2234        // set fields that where undefined before this addition, back to undefined.
2235
for (int i = YEAR; i <= SECOND; i++) {
2236            if (fieldUndefined[i]) {
2237                switch (i) {
2238                case YEAR:
2239                    setYear(DatatypeConstants.FIELD_UNDEFINED);
2240                    break;
2241                case MONTH:
2242                    setMonth(DatatypeConstants.FIELD_UNDEFINED);
2243                    break;
2244                case DAY:
2245                    setDay(DatatypeConstants.FIELD_UNDEFINED);
2246                    break;
2247                case HOUR:
2248                    setHour(DatatypeConstants.FIELD_UNDEFINED);
2249                    break;
2250                case MINUTE:
2251                    setMinute(DatatypeConstants.FIELD_UNDEFINED);
2252                    break;
2253                case SECOND:
2254                    setSecond(DatatypeConstants.FIELD_UNDEFINED);
2255                    setFractionalSecond(null);
2256                    break;
2257                }
2258            }
2259        }
2260    }
2261
2262    private static final BigInteger JavaDoc FOUR = BigInteger.valueOf(4);
2263    private static final BigInteger JavaDoc HUNDRED = BigInteger.valueOf(100);
2264    private static final BigInteger JavaDoc FOUR_HUNDRED = BigInteger.valueOf(400);
2265    private static final BigInteger JavaDoc SIXTY = BigInteger.valueOf(60);
2266    private static final BigInteger JavaDoc TWENTY_FOUR = BigInteger.valueOf(24);
2267    private static final BigInteger JavaDoc TWELVE = BigInteger.valueOf(12);
2268    private static final BigDecimal JavaDoc DECIMAL_ZERO = new BigDecimal JavaDoc("0");
2269    private static final BigDecimal JavaDoc DECIMAL_ONE = new BigDecimal JavaDoc("1");
2270    private static final BigDecimal JavaDoc DECIMAL_SIXTY = new BigDecimal JavaDoc("60");
2271
2272
2273    private static int daysInMonth[] = { 0, // XML Schema months start at 1.
2274
31, 28, 31, 30, 31, 30,
2275                                       31, 31, 30, 31, 30, 31};
2276
2277    private static int maximumDayInMonthFor(BigInteger JavaDoc year, int month) {
2278    if (month != DatatypeConstants.FEBRUARY) {
2279        return daysInMonth[month];
2280    } else {
2281        if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
2282        (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
2283         year.mod(FOUR).equals(BigInteger.ZERO))) {
2284        // is a leap year.
2285
return 29;
2286        } else {
2287        return daysInMonth[month];
2288        }
2289    }
2290    }
2291
2292    private static int maximumDayInMonthFor(int year, int month) {
2293    if (month != DatatypeConstants.FEBRUARY) {
2294        return daysInMonth[month];
2295    } else {
2296        if ( ((year %400) == 0) ||
2297         ( ((year % 100) != 0) && ((year % 4) == 0))) {
2298        // is a leap year.
2299
return 29;
2300        } else {
2301        return daysInMonth[DatatypeConstants.FEBRUARY];
2302        }
2303    }
2304    }
2305
2306    /**
2307     * <p>Convert <code>this</code> to <code>java.util.GregorianCalendar</code>.</p>
2308     *
2309     * <p>When <code>this</code> instance has an undefined field, this
2310     * conversion relies on the <code>java.util.GregorianCalendar</code> default
2311     * for its corresponding field. A notable difference between
2312     * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
2313     * is that Timezone value is optional for date/time datatypes and it is
2314     * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
2315     * for <code>java.util.TimeZone.getDefault()</code> on how the default
2316     * is determined. To explicitly specify the <code>TimeZone</code>
2317     * instance, see
2318     * {@link #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)}.</p>
2319     *
2320     * <table border="2" rules="all" cellpadding="2">
2321     * <thead>
2322     * <tr>
2323     * <th align="center" colspan="2">
2324     * Field by Field Conversion from this class to
2325     * <code>java.util.GregorianCalendar</code>
2326     * </th>
2327     * </tr>
2328     * </thead>
2329     * <tbody>
2330     * <tr>
2331     * <th><code>java.util.GregorianCalendar</code> field</th>
2332     * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
2333     * </tr>
2334     * <tr>
2335     * <th><code>ERA</code></th>
2336     * <th>{@link #getEonAndYear()}<code>.signum() < 0 ? GregorianCalendar.BC : GregorianCalendar.AD</code></th>
2337     * </tr>
2338     * <tr>
2339     * <th><code>YEAR</code></th>
2340     * <th>{@link #getEonAndYear()}<code>.abs().intValue()</code><i>*</i></th>
2341     * </tr>
2342     * <tr>
2343     * <th><code>MONTH</code></th>
2344     * <th>{@link #getMonth()}<code> - 1</code></th>
2345     * </tr>
2346     * <tr>
2347     * <th><code>DAY_OF_MONTH</code></th>
2348     * <th>{@link #getDay()}</th>
2349     * </tr>
2350     * <tr>
2351     * <th><code>AM_PM</code></th>
2352     * <th>{@link #getHour()} < 12 : Calendar.AM : Calendar.PM</th>
2353     * </tr>
2354     * <tr>
2355     * <th><code>HOUR_OF_DAY</code></th>
2356     * <th>{@link #getHour()}</th>
2357     * </tr>
2358     * <tr>
2359     * <th><code>MINUTE</code></th>
2360     * <th>{@link #getMinute()}</th>
2361     * </tr>
2362     * <tr>
2363     * <th><code>SECOND</code></th>
2364     * <th>{@link #getSecond()}</th>
2365     * </tr>
2366     * <tr>
2367     * <th><code>MILLISECOND</code></th>
2368     * <th>get millisecond order from {@link #getFractionalSecond()}<i>*</i> </th>
2369     * </tr>
2370     * <tr>
2371     * <th><code>GregorianCalendar.setTimeZone(TimeZone)</code></th>
2372     * <th>{@link #getTimezone()} formatted into Custom timezone id</th>
2373     * </tr>
2374     * </tbody>
2375     * </table>
2376     * <i>*</i> designates possible loss of precision during the conversion due
2377     * to source datatype having higer precison than target datatype.
2378     *
2379     * <p>To ensure consistency in conversion implementations, the new
2380     * <code>GregorianCalendar</code> should be instantiated in following
2381     * manner.
2382     * <ul>
2383     * <li>Using <code>timeZone</code> value as defined above, create a new
2384     * <code>java.util.GregorianCalendar(timeZone,Locale.getDefault())</code>.
2385     * </li>
2386     * <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2387     * <li>Obtain a pure Gregorian Calendar by invoking
2388     * <code>GregorianCalendar.setGregorianChange(
2389     * new Date(Long.MIN_VALUE))</code>.</li>
2390     * <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2391     * MINUTE, SECOND and MILLISECOND are set using the method
2392     * <code>Calendar.set(int,int)</code></li>
2393     * </ul>
2394     * </p>
2395     *
2396     * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, XMLGregorianCalendar)
2397     */

2398    public java.util.GregorianCalendar JavaDoc toGregorianCalendar() {
2399
2400    GregorianCalendar JavaDoc result = null;
2401    final int DEFAULT_TIMEZONE_OFFSET = DatatypeConstants.FIELD_UNDEFINED;
2402    TimeZone JavaDoc tz = getTimeZone(DEFAULT_TIMEZONE_OFFSET);
2403    Locale JavaDoc locale = java.util.Locale.getDefault();
2404
2405    result = new GregorianCalendar JavaDoc(tz, locale);
2406    result.clear();
2407    result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2408    
2409    // if year( and eon) are undefined, leave default Calendar values
2410
BigInteger JavaDoc year = getEonAndYear();
2411    if (year != null) {
2412        result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2413        result.set(Calendar.YEAR, year.abs().intValue());
2414    }
2415    
2416    // only set month if it is set
2417
if (month != DatatypeConstants.FIELD_UNDEFINED) {
2418        // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2419
result.set(Calendar.MONTH, month - 1);
2420    }
2421    
2422    // only set day if it is set
2423
if (day != DatatypeConstants.FIELD_UNDEFINED) {
2424        result.set(Calendar.DAY_OF_MONTH, day);
2425    }
2426    
2427    // only set hour if it is set
2428
if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2429        result.set(Calendar.HOUR_OF_DAY, hour);
2430    }
2431    
2432    // only set minute if it is set
2433
if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2434        result.set(Calendar.MINUTE, minute);
2435    }
2436    
2437    // only set second if it is set
2438
if (second != DatatypeConstants.FIELD_UNDEFINED) {
2439        result.set(Calendar.SECOND, second);
2440    }
2441    
2442    // only set millisend if it is set
2443
if (fractionalSecond != null) {
2444        result.set(Calendar.MILLISECOND, getMillisecond());
2445    }
2446
2447    return result;
2448    }
2449
2450    /**
2451     * <p>Convert <code>this</code> along with provided parameters
2452     * to <code>java.util.GregorianCalendar</code> instance.</p>
2453     *
2454     * <p> Since XML Schema 1.0 date/time datetypes has no concept of
2455     * timezone ids or daylight savings timezone ids, this conversion operation
2456     * allows the user to explicitly specify one with
2457     * <code>timezone</code> parameter.</p>
2458     *
2459     * <p>To compute the return value's <code>TimeZone</code> field,
2460     * <ul>
2461     * <li>when parameter <code>timeZone</code> is non-null,
2462     * it is the timezone field.</li>
2463     * <li>else when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2464     * create a <code>java.util.TimeZone</code> with a custom timezone id
2465     * using the <code>this.getTimezone()</code>.</li>
2466     * <li>else when <code>defaults.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2467     * create a <code>java.util.TimeZone</code> with a custom timezone id
2468     * using <code>defaults.getTimezone()</code>.</li>
2469     * <li>else use the <code>GregorianCalendar</code> default timezone value
2470     * for the host is definedas specified by
2471     * <code>java.util.TimeZone.getDefault()</code>.</li></p>
2472     *
2473     * <p>To ensure consistency in conversion implementations, the new
2474     * <code>GregorianCalendar</code> should be instantiated in following
2475     * manner.
2476     * <ul>
2477     * <li>Create a new <code>java.util.GregorianCalendar(TimeZone,
2478     * Locale)</code> with TimeZone set as specified above and the
2479     * <code>Locale</code> parameter.
2480     * </li>
2481     * <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2482     * <li>Obtain a pure Gregorian Calendar by invoking
2483     * <code>GregorianCalendar.setGregorianChange(
2484     * new Date(Long.MIN_VALUE))</code>.</li>
2485     * <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2486     * MINUTE, SECOND and MILLISECOND are set using the method
2487     * <code>Calendar.set(int,int)</code></li>
2488     * </ul>
2489     *
2490     * @param timezone provide Timezone. <code>null</code> is a legal value.
2491     * @param aLocale provide explicit Locale. Use default GregorianCalendar locale if
2492     * value is <code>null</code>.
2493     * @param defaults provide default field values to use when corresponding
2494     * field for this instance is DatatypeConstants.FIELD_UNDEFINED or null.
2495     * If <code>defaults</code>is <code>null</code> or a field
2496     * within the specified <code>defaults</code> is undefined,
2497     * just use <code>java.util.GregorianCalendar</code> defaults.
2498     * @return a java.util.GregorianCalendar conversion of this instance.
2499     *
2500     * @see #LEAP_YEAR_DEFAULT
2501     */

2502    public GregorianCalendar JavaDoc toGregorianCalendar(java.util.TimeZone JavaDoc timezone,
2503                     java.util.Locale JavaDoc aLocale,
2504                     XMLGregorianCalendar JavaDoc defaults) {
2505    GregorianCalendar JavaDoc result = null;
2506    TimeZone JavaDoc tz = timezone;
2507    if (tz == null) {
2508        int defaultZoneoffset = DatatypeConstants.FIELD_UNDEFINED;
2509        if (defaults != null) {
2510            defaultZoneoffset = defaults.getTimezone();
2511        }
2512        tz = getTimeZone(defaultZoneoffset);
2513    }
2514    if (aLocale == null) {
2515        aLocale = java.util.Locale.getDefault();
2516    }
2517    result = new GregorianCalendar JavaDoc(tz, aLocale);
2518    result.clear();
2519    result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2520
2521    // if year( and eon) are undefined, leave default Calendar values
2522
BigInteger JavaDoc year = getEonAndYear();
2523    if (year != null) {
2524        result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2525        result.set(Calendar.YEAR, year.abs().intValue());
2526    } else {
2527        // use default if set
2528
BigInteger JavaDoc defaultYear = (defaults != null) ? defaults.getEonAndYear() : null;
2529        if (defaultYear != null) {
2530            result.set(Calendar.ERA, defaultYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2531            result.set(Calendar.YEAR, defaultYear.abs().intValue());
2532        }
2533    }
2534    
2535    // only set month if it is set
2536
if (month != DatatypeConstants.FIELD_UNDEFINED) {
2537        // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2538
result.set(Calendar.MONTH, month - 1);
2539    } else {
2540        // use default if set
2541
int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
2542        if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
2543            // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2544
result.set(Calendar.MONTH, defaultMonth - 1);
2545        }
2546    }
2547        
2548    // only set day if it is set
2549
if (day != DatatypeConstants.FIELD_UNDEFINED) {
2550        result.set(Calendar.DAY_OF_MONTH, day);
2551    } else {
2552        // use default if set
2553
int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
2554        if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
2555            result.set(Calendar.DAY_OF_MONTH, defaultDay);
2556        }
2557    }
2558    
2559    // only set hour if it is set
2560
if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2561        result.set(Calendar.HOUR_OF_DAY, hour);
2562    } else {
2563        // use default if set
2564
int defaultHour = (defaults != null) ? defaults.getHour() : DatatypeConstants.FIELD_UNDEFINED;
2565        if (defaultHour != DatatypeConstants.FIELD_UNDEFINED) {
2566            result.set(Calendar.HOUR_OF_DAY, defaultHour);
2567        }
2568    }
2569    
2570    // only set minute if it is set
2571
if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2572        result.set(Calendar.MINUTE, minute);
2573    } else {
2574        // use default if set
2575
int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
2576        if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
2577            result.set(Calendar.MINUTE, defaultMinute);
2578        }
2579    }
2580    
2581    // only set second if it is set
2582
if (second != DatatypeConstants.FIELD_UNDEFINED) {
2583        result.set(Calendar.SECOND, second);
2584    } else {
2585        // use default if set
2586
int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
2587        if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
2588            result.set(Calendar.SECOND, defaultSecond);
2589        }
2590    }
2591    
2592    // only set millisend if it is set
2593
if (fractionalSecond != null) {
2594        result.set(Calendar.MILLISECOND, getMillisecond());
2595    } else {
2596        // use default if set
2597
BigDecimal JavaDoc defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
2598        if (defaultFractionalSecond != null) {
2599            result.set(Calendar.MILLISECOND, defaults.getMillisecond());
2600        }
2601    }
2602
2603    return result;
2604    }
2605
2606    /**
2607     * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
2608     *
2609     * <p>If timezone field is defined for this instance,
2610     * returns TimeZone initialized with custom timezone id
2611     * of zoneoffset. If timezone field is undefined,
2612     * try the defaultZoneoffset that was passed in.
2613     * If defaultZoneoffset is DatatypeConstants.FIELD_UNDEFINED, return
2614     * default timezone for this host.
2615     * (Same default as java.util.GregorianCalendar).</p>
2616     *
2617     * @param defaultZoneoffset default zoneoffset if this zoneoffset is
2618     * {@link DatatypeConstants#FIELD_UNDEFINED}.
2619     *
2620     * @return TimeZone for this.
2621     */

2622    public TimeZone JavaDoc getTimeZone(int defaultZoneoffset) {
2623    TimeZone JavaDoc result = null;
2624    int zoneoffset = getTimezone();
2625
2626    if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2627        zoneoffset = defaultZoneoffset;
2628    }
2629    if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2630        result = TimeZone.getDefault();
2631    } else {
2632        // zoneoffset is in minutes. Convert to custom timezone id format.
2633
char sign = zoneoffset < 0 ? '-' : '+';
2634        if (sign == '-') {
2635        zoneoffset = -zoneoffset;
2636        }
2637        int hour = zoneoffset / 60;
2638        int minutes = zoneoffset - (hour * 60);
2639        
2640        // Javadoc for java.util.TimeZone documents max length
2641
// for customTimezoneId is 8 when optional ':' is not used.
2642
// Format is
2643
// "GMT" ('-'|''+') (digit digit?) (digit digit)?
2644
// hour minutes
2645
StringBuffer JavaDoc customTimezoneId = new StringBuffer JavaDoc(8);
2646        customTimezoneId.append("GMT");
2647        customTimezoneId.append(sign);
2648        customTimezoneId.append(hour);
2649        if (minutes != 0) {
2650        customTimezoneId.append(minutes);
2651        }
2652        result = TimeZone.getTimeZone(customTimezoneId.toString());
2653    }
2654    return result;
2655    }
2656    
2657    /**
2658     * <p>Creates and returns a copy of this object.</p>
2659     *
2660     * @return copy of this <code>Object</code>
2661     */

2662   public Object JavaDoc clone() {
2663        // Both this.eon and this.fractionalSecond are instances
2664
// of immutable classes, so they do not need to be cloned.
2665
return new XMLGregorianCalendarImpl(getEonAndYear(),
2666                        this.month, this.day,
2667            this.hour, this.minute, this.second,
2668            this.fractionalSecond,
2669            this.timezone);
2670    }
2671
2672    /**
2673     * <p>Unset all fields to undefined.</p>
2674     *
2675     * <p>Set all int fields to {@link DatatypeConstants#FIELD_UNDEFINED} and reference fields
2676     * to null.</p>
2677     */

2678    public void clear() {
2679    eon = null;
2680    year = DatatypeConstants.FIELD_UNDEFINED;
2681    month = DatatypeConstants.FIELD_UNDEFINED;
2682    day = DatatypeConstants.FIELD_UNDEFINED;
2683    timezone = DatatypeConstants.FIELD_UNDEFINED; // in minutes
2684
hour = DatatypeConstants.FIELD_UNDEFINED;
2685    minute = DatatypeConstants.FIELD_UNDEFINED;
2686    second = DatatypeConstants.FIELD_UNDEFINED;
2687    fractionalSecond = null;
2688    }
2689
2690    public void setMillisecond(int millisecond) {
2691    if (millisecond == DatatypeConstants.FIELD_UNDEFINED) {
2692        fractionalSecond = null;
2693    } else {
2694        checkFieldValueConstraint(MILLISECOND, millisecond);
2695        fractionalSecond = new BigDecimal JavaDoc((long) millisecond).movePointLeft(3);
2696    }
2697    }
2698
2699    public void setFractionalSecond(BigDecimal JavaDoc fractional) {
2700    if (fractional != null) {
2701        if ((fractional.compareTo(DECIMAL_ZERO) < 0) ||
2702        (fractional.compareTo(DECIMAL_ONE) > 0)) {
2703        throw new IllegalArgumentException JavaDoc(DatatypeMessageFormatter.formatMessage(null,
2704            "InvalidFractional", new Object JavaDoc[]{fractional}));
2705        }
2706    }
2707    this.fractionalSecond = fractional;
2708    }
2709
2710    private final class Parser {
2711        private final String JavaDoc format;
2712        private final String JavaDoc value;
2713
2714        private final int flen;
2715        private final int vlen;
2716
2717        private int fidx;
2718        private int vidx;
2719
2720        private Parser(String JavaDoc format, String JavaDoc value) {
2721            this.format = format;
2722            this.value = value;
2723            this.flen = format.length();
2724            this.vlen = value.length();
2725        }
2726        
2727        /**
2728         * <p>Parse a formated <code>String</code> into an <code>XMLGregorianCalendar</code>.</p>
2729         *
2730         * <p>If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value,
2731         * an <code>IllegalArgumentException</code> is thrown.</p>
2732         *
2733         * @throws IllegalArgumentException If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value.
2734         */

2735        public void parse() throws IllegalArgumentException JavaDoc {
2736            while (fidx < flen) {
2737                char fch = format.charAt(fidx++);
2738
2739                if (fch != '%') { // not a meta character
2740
skip(fch);
2741                    continue;
2742                }
2743
2744                // seen meta character. we don't do error check against the format
2745
switch (format.charAt(fidx++)) {
2746                    case 'Y' : // year
2747
setYear(parseBigInteger(4));
2748                        break;
2749
2750                    case 'M' : // month
2751
setMonth(parseInt(2, 2));
2752                        break;
2753
2754                    case 'D' : // days
2755
setDay(parseInt(2, 2));
2756                        break;
2757
2758                    case 'h' : // hours
2759
setHour(parseInt(2, 2));
2760                        break;
2761
2762                    case 'm' : // minutes
2763
setMinute(parseInt(2, 2));
2764                        break;
2765
2766                    case 's' : // parse seconds.
2767
setSecond(parseInt(2, 2));
2768
2769                        if (peek() == '.') {
2770                setFractionalSecond(parseBigDecimal());
2771                        }
2772                        break;
2773
2774                    case 'z' : // time zone. missing, 'Z', or [+-]nn:nn
2775
char vch = peek();
2776                        if (vch == 'Z') {
2777                            vidx++;
2778                            setTimezone(0);
2779                        } else if (vch == '+' || vch == '-') {
2780                            vidx++;
2781                            int h = parseInt(2, 2);
2782                            skip(':');
2783                            int m = parseInt(2, 2);
2784                            setTimezone((h * 60 + m) * (vch == '+' ? 1 : -1));
2785                        }
2786            
2787                        break;
2788
2789                    default :
2790                        // illegal meta character. impossible.
2791
throw new InternalError JavaDoc();
2792                }
2793            }
2794
2795            if (vidx != vlen) {
2796                // some tokens are left in the input
2797
throw new IllegalArgumentException JavaDoc(value); //,vidx);
2798
}
2799        }
2800        
2801        private char peek() throws IllegalArgumentException JavaDoc {
2802            if (vidx == vlen) {
2803                return (char) -1;
2804            }
2805            return value.charAt(vidx);
2806        }
2807        
2808        private char read() throws IllegalArgumentException JavaDoc {
2809            if (vidx == vlen) {
2810                throw new IllegalArgumentException JavaDoc(value); //,vidx);
2811
}
2812            return value.charAt(vidx++);
2813        }
2814        
2815        private void skip(char ch) throws IllegalArgumentException JavaDoc {
2816            if (read() != ch) {
2817                throw new IllegalArgumentException JavaDoc(value); //,vidx-1);
2818
}
2819        }
2820        
2821        private int parseInt(int minDigits, int maxDigits)
2822            throws IllegalArgumentException JavaDoc {
2823            int vstart = vidx;
2824            while (isDigit(peek()) && (vidx - vstart) <= maxDigits) {
2825                vidx++;
2826            }
2827            if ((vidx - vstart) < minDigits) {
2828                // we are expecting more digits
2829
throw new IllegalArgumentException JavaDoc(value); //,vidx);
2830
}
2831
2832            // NumberFormatException is IllegalArgumentException
2833
// try {
2834
return Integer.parseInt(value.substring(vstart, vidx));
2835            // } catch( NumberFormatException e ) {
2836
// // if the value is too long for int, NumberFormatException is thrown
2837
// throw new IllegalArgumentException(value,vstart);
2838
// }
2839
}
2840
2841        private BigInteger JavaDoc parseBigInteger(int minDigits)
2842            throws IllegalArgumentException JavaDoc {
2843            int vstart = vidx;
2844
2845        // skip leading negative, if it exists
2846
if (peek() == '-') {
2847        vidx++;
2848        }
2849            while (isDigit(peek())) {
2850                vidx++;
2851            }
2852            if ((vidx - vstart) < minDigits) {
2853                // we are expecting more digits
2854
throw new IllegalArgumentException JavaDoc(value); //,vidx);
2855
}
2856
2857            return new BigInteger JavaDoc(value.substring(vstart, vidx));
2858        }
2859
2860        private BigDecimal JavaDoc parseBigDecimal()
2861            throws IllegalArgumentException JavaDoc {
2862            int vstart = vidx;
2863
2864        if (peek() == '.') {
2865        vidx++;
2866        } else {
2867                throw new IllegalArgumentException JavaDoc(value);
2868        }
2869            while (isDigit(peek())) {
2870                vidx++;
2871            }
2872            return new BigDecimal JavaDoc(value.substring(vstart, vidx));
2873        }
2874    }
2875
2876    private static boolean isDigit(char ch) {
2877        return '0' <= ch && ch <= '9';
2878    }
2879
2880    private String JavaDoc format( String JavaDoc format ) {
2881        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2882        int fidx=0,flen=format.length();
2883        
2884        while(fidx<flen) {
2885            char fch = format.charAt(fidx++);
2886            if(fch!='%') {// not a meta char
2887
buf.append(fch);
2888                continue;
2889            }
2890            
2891            switch(format.charAt(fidx++)) {
2892            case 'Y':
2893                printNumber(buf,getEonAndYear(), 4);
2894                break;
2895            case 'M':
2896                printNumber(buf,getMonth(),2);
2897                break;
2898            case 'D':
2899                printNumber(buf,getDay(),2);
2900                break;
2901            case 'h':
2902                printNumber(buf,getHour(),2);
2903                break;
2904            case 'm':
2905                printNumber(buf,getMinute(),2);
2906                break;
2907            case 's':
2908                printNumber(buf,getSecond(),2);
2909        if (getFractionalSecond() != null) {
2910            String JavaDoc frac = getFractionalSecond().toString();
2911            //skip leading zero.
2912
buf.append(frac.substring(1, frac.length()));
2913        }
2914                break;
2915            case 'z':
2916        int offset = getTimezone();
2917                if(offset == 0) {
2918            buf.append('Z');
2919        } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
2920            if(offset<0) {
2921            buf.append('-');
2922            offset *= -1;
2923            } else {
2924            buf.append('+');
2925            }
2926            printNumber(buf,offset/60,2);
2927                    buf.append(':');
2928                    printNumber(buf,offset%60,2);
2929                }
2930                break;
2931            default:
2932                throw new InternalError JavaDoc(); // impossible
2933
}
2934        }
2935        
2936        return buf.toString();
2937    }
2938    
2939    /**
2940     * Prints an integer as a String.
2941     *
2942     * @param out
2943     * The formatted string will be appended into this buffer.
2944     * @param number
2945     * The integer to be printed.
2946     * @param nDigits
2947     * The field will be printed by using at least this
2948     * number of digits. For example, 5 will be printed as "0005"
2949     * if nDigits==4.
2950     */

2951    private void printNumber( StringBuffer JavaDoc out, int number, int nDigits ) {
2952        String JavaDoc s = String.valueOf(number);
2953        for( int i=s.length(); i<nDigits; i++ )
2954            out.append('0');
2955        out.append(s);
2956    }
2957
2958    /**
2959     * Prints an BigInteger as a String.
2960     *
2961     * @param out
2962     * The formatted string will be appended into this buffer.
2963     * @param number
2964     * The integer to be printed.
2965     * @param nDigits
2966     * The field will be printed by using at least this
2967     * number of digits. For example, 5 will be printed as "0005"
2968     * if nDigits==4.
2969     */

2970    private void printNumber( StringBuffer JavaDoc out, BigInteger JavaDoc number, int nDigits) {
2971        String JavaDoc s = number.toString();
2972        for( int i=s.length(); i<nDigits; i++ )
2973            out.append('0');
2974        out.append(s);
2975    }
2976
2977    /**
2978     * Compute <code>value*signum</code> where value==null is treated as
2979     * value==0.
2980     * @return non-null {@link BigInteger}.
2981     */

2982    static BigInteger JavaDoc sanitize(Number JavaDoc value, int signum) {
2983        if (signum == 0 || value == null) {
2984            return BigInteger.ZERO;
2985        }
2986        return (signum < 0)? ((BigInteger JavaDoc)value).negate() : (BigInteger JavaDoc)value;
2987    }
2988
2989    /** <p><code>reset()</code> is designed to allow the reuse of existing
2990     * <code>XMLGregorianCalendar</code>s thus saving resources associated
2991     * with the creation of new <code>XMLGregorianCalendar</code>s.</p>
2992     */

2993    public void reset() {
2994        //PENDING : Implementation of reset method
2995
}
2996}
2997
2998
Popular Tags