8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.org.apache.xerces.internal.jaxp.datatype;
27
28 import java.io.Serializable;
29 import java.math.BigDecimal;
30 import java.math.BigInteger;
31 import java.util.TimeZone;
32 import java.util.Calendar;
33 import java.util.GregorianCalendar;
34 import java.util.Date;
35 import java.util.Locale;
36
37 import javax.xml.datatype.DatatypeConstants;
38 import javax.xml.datatype.Duration;
39 import javax.xml.datatype.XMLGregorianCalendar;
40 import javax.xml.namespace.QName;
41 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
42 import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
43
44 /**
45 * <p>Representation for W3C XML Schema 1.0 date/time datatypes.
46 * Specifically, these date/time datatypes are
47 * {@link DatatypeConstants#DATETIME dateTime},
178 * <li>partial order relation comparator method, {@link #compare(XMLGregorianCalendar)}</li>
179 * <li>{@link #equals(Object)} defined relative to {@link #compare(XMLGregorianCalendar)}.</li>
180 * <li> addition operation with {@link javax.xml.datatype.Duration}.
181 * instance as defined in <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">
182 * W3C XML Schema 1.0 Part 2, Appendix E, <i>Adding durations to dateTimes</i></a>.</li>
183 * </ul>
184 * </p>
185 *
186 * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
187 * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
188 * @author <a href="mailto:Sunitha.Reddy@Sun.com">Sunitha Reddy</a>
189 * @version $Revision: 1.14 $, $Date: 2010-11-10 07:41:41 $
190 * @see javax.xml.datatype.Duration
191 * @since 1.5
192 */
193
194 public class XMLGregorianCalendarImpl
195 extends XMLGregorianCalendar
196 implements Serializable, Cloneable {
197
198 /**
199 * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
200 */
201 private BigInteger eon = null;
202
203 /**
204 * <p>Year of this <code>XMLGregorianCalendar</code>.</p>
205 */
206 private int year = DatatypeConstants.FIELD_UNDEFINED;
207
208 /**
209 * <p>Month of this <code>XMLGregorianCalendar</code>.</p>
210 */
211 private int month = DatatypeConstants.FIELD_UNDEFINED;
212
213 /**
214 * <p>Day of this <code>XMLGregorianCalendar</code>.</p>
215 */
216 private int day = DatatypeConstants.FIELD_UNDEFINED;
217
224 * <p>Hour of this <code>XMLGregorianCalendar</code>.</p>
225 */
226 private int hour = DatatypeConstants.FIELD_UNDEFINED;
227
228 /**
229 * <p>Minute of this <code>XMLGregorianCalendar</code>.</p>
230 */
231 private int minute = DatatypeConstants.FIELD_UNDEFINED;
232
233 /**
234 * <p>Second of this <code>XMLGregorianCalendar</code>.</p>
235 */
236 private int second = DatatypeConstants.FIELD_UNDEFINED ;
237
238 /**
239 * <p>Fractional second of this <code>XMLGregorianCalendar</code>.</p>
240 */
241 private BigDecimal fractionalSecond = null;
242
243 /**
244 * <p>Constant to represent a billion.</p>
245 */
246 private static final BigInteger BILLION = new BigInteger("1000000000");
247
248 /**
249 * <p>Obtain a pure Gregorian Calendar by calling
250 * GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE). </p>
251 */
252 private static final Date PURE_GREGORIAN_CHANGE =
253 new Date(Long.MIN_VALUE);
254
255 /**
256 * Year index for MIN_ and MAX_FIELD_VALUES.
257 */
258 private static final int YEAR = 0;
259
260 /**
261 * Month index for MIN_ and MAX_FIELD_VALUES.
262 */
263 private static final int MONTH = 1;
264
265 /**
266 * Day index for MIN_ and MAX_FIELD_VALUES.
267 */
268 private static final int DAY = 2;
424 format = "%Y" + "%z";
425 } else if (countSeparator == 1) {
426 // GYearMonth
427 format = "%Y-%M" + "%z";
428 } else {
429 // Date or invalid lexicalRepresentation
430 // Fix 4971612: invalid SCCS macro substitution in data string
431 format = "%Y-%M-%D" + "%z";
432 }
433 }
434 Parser p = new Parser(format, lexRep);
435 p.parse();
436
437 // check for validity
438 if (!isValid()) {
439 throw new IllegalArgumentException(
440 DatatypeMessageFormatter.formatMessage(null, "InvalidXGCRepresentation", new Object[]{lexicalRepresentation})
441 //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
442 );
443 }
444 }
445
446 /**
447 * <p>Create an instance with all date/time datatype fields set to
448 * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.</p>
449 */
450 public XMLGregorianCalendarImpl() {
451
452 // field initializers already do the correct initialization.
453 }
454
455 /**
456 * <p>Private constructor allowing for complete value spaces allowed by
457 * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
458 * builtin datatypes. Note that <code>year</code> parameter supports
459 * arbitrarily large numbers and fractionalSecond has infinite
460 * precision.</p>
461 *
462 * @param year of <code>XMLGregorianCalendar</code> to be created.
463 * @param month of <code>XMLGregorianCalendar</code> to be created.
464 * @param day of <code>XMLGregorianCalendar</code> to be created.
465 * @param hour of <code>XMLGregorianCalendar</code> to be created.
466 * @param minute of <code>XMLGregorianCalendar</code> to be created.
504 String fractionalSecondString = "null";
505 if (fractionalSecond != null) {
506 fractionalSecondString = fractionalSecond.toString();
507 }
508
509 throw new IllegalArgumentException(
510 "year = " + yearString
511 + ", month = " + month
512 + ", day = " + day
513 + ", hour = " + hour
514 + ", minute = " + minute
515 + ", second = " + second
516 + ", fractionalSecond = " + fractionalSecondString
517 + ", timezone = " + timezone
518 + ", is not a valid representation of an XML Gregorian Calendar value."
519 );
520 */
521
522 }
523
524 }
525
526 /**
527 * <p>Private constructor of value spaces that a
528 * <code>java.util.GregorianCalendar</code> instance would need to convert to an
529 * <code>XMLGregorianCalendar</code> instance.</p>
530 *
531 * <p><code>XMLGregorianCalendar eon</code> and
532 * <code>fractionalSecond</code> are set to <code>null</code></p>
533 *
534 * @param year of <code>XMLGregorianCalendar</code> to be created.
535 * @param month of <code>XMLGregorianCalendar</code> to be created.
536 * @param day of <code>XMLGregorianCalendar</code> to be created.
537 * @param hour of <code>XMLGregorianCalendar</code> to be created.
538 * @param minute of <code>XMLGregorianCalendar</code> to be created.
539 * @param second of <code>XMLGregorianCalendar</code> to be created.
540 * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
541 * @param timezone of <code>XMLGregorianCalendar</code> to be created.
542 */
543 private XMLGregorianCalendarImpl(
544 int year,
545 int month,
546 int day,
547 int hour,
548 int minute,
549 int second,
550 int millisecond,
551 int timezone) {
552
553 setYear(year);
554 setMonth(month);
555 setDay(day);
556 setTime(hour, minute, second);
557 setTimezone(timezone);
558 setMillisecond(millisecond);
559
560 if (!isValid()) {
561
562 throw new IllegalArgumentException(
563 DatatypeMessageFormatter.formatMessage(null,
564 "InvalidXGCValue-milli",
565 new Object[] { new Integer(year), new Integer(month), new Integer(day),
566 new Integer(hour), new Integer(minute), new Integer(second),
567 new Integer(millisecond), new Integer(timezone)})
568 );
569 /*
570 throw new IllegalArgumentException(
571 "year = " + year
572 + ", month = " + month
573 + ", day = " + day
574 + ", hour = " + hour
575 + ", minute = " + minute
576 + ", second = " + second
577 + ", millisecond = " + millisecond
578 + ", timezone = " + timezone
579 + ", is not a valid representation of an XML Gregorian Calendar value."
580 );
581 */
582
583 }
584 }
585
586 /**
587 * <p>Convert a <code>java.util.GregorianCalendar</code> to XML Schema 1.0
588 * representation.</p>
589 *
590 * <table border="2" rules="all" cellpadding="2">
591 * <thead>
592 * <tr>
593 * <th align="center" colspan="2">
594 * Field by Field Conversion from
595 * <code>java.util.GregorianCalendar</code> to this class
596 * </th>
597 * </tr>
598 * </thead>
599 * <tbody>
600 * <tr>
601 * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
602 * <th><code>java.util.GregorianCalendar</code> field</th>
603 * </tr>
644
645 int year = cal.get(Calendar.YEAR);
646 if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
647 year = -year;
648 }
649 this.setYear(year);
650
651 // Calendar.MONTH is zero based, XSD Date datatype's month field starts
652 // with JANUARY as 1.
653 this.setMonth(cal.get(Calendar.MONTH) + 1);
654 this.setDay(cal.get(Calendar.DAY_OF_MONTH));
655 this.setTime(
656 cal.get(Calendar.HOUR_OF_DAY),
657 cal.get(Calendar.MINUTE),
658 cal.get(Calendar.SECOND),
659 cal.get(Calendar.MILLISECOND));
660
661 // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
662 int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
663 this.setTimezone(offsetInMinutes);
664 }
665
666 // Factories
667
668 /**
669 * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
670 * All possible fields are specified for this factory method.</p>
671 *
672 * @param year represents both high-order eons and low-order year.
673 * @param month of <code>dateTime</code>
674 * @param day of <code>dateTime</code>
675 * @param hours of <code>dateTime</code>
676 * @param minutes of <code>dateTime</code>
677 * @param seconds of <code>dateTime</code>
678 * @param fractionalSecond value of null indicates optional field is absent.
679 * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
680 *
681 * @return <code>XMLGregorianCalendar</code> created from parameter values.
682 *
683 * @see DatatypeConstants#FIELD_UNDEFINED
1147 }
1148
1149 // setters
1150
1151 /**
1152 * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
1153 *
1154 * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
1155 *
1156 * @param year value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1157 *
1158 * @throws IllegalArgumentException if <code>year</code> parameter is
1159 * outside value constraints for the field as specified in
1160 * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1161 */
1162 public void setYear(BigInteger year) {
1163 if (year == null) {
1164 this.eon = null;
1165 this.year = DatatypeConstants.FIELD_UNDEFINED;
1166 } else {
1167 BigInteger temp = year.remainder(BILLION);
1168 this.year = temp.intValue();
1169 setEon(year.subtract(temp));
1170 }
1171 }
1172
1173 /**
1174 * <p>Set year of XSD <code>dateTime</code> year field.</p>
1175 *
1176 * <p>Unset this field by invoking the setter with a parameter value of
1177 * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1178 *
1179 * <p>Note: if the absolute value of the <code>year</code> parameter
1180 * is less than 10^9, the eon component of the XSD year field is set to
1181 * <code>null</code> by this method.</p>
1182 *
1183 * @param year value constraints are summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1184 * If year is {@link DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
1185 */
1186 public void setYear(int year) {
1187 if (year == DatatypeConstants.FIELD_UNDEFINED) {
1188 this.year = DatatypeConstants.FIELD_UNDEFINED;
1189 this.eon = null;
1190 } else if (Math.abs(year) < BILLION.intValue()) {
1191 this.year = year;
1192 this.eon = null;
1193 } else {
1194 BigInteger theYear = BigInteger.valueOf((long) year);
1195 BigInteger remainder = theYear.remainder(BILLION);
1196 this.year = remainder.intValue();
1197 setEon(theYear.subtract(remainder));
1198 }
1199 }
1200
1201 /**
1202 * <p>Set high order part of XSD <code>dateTime</code> year field.</p>
1203 *
1204 * <p>Unset this field by invoking the setter with a parameter value of
1205 * <code>null</code>.</p>
1206 *
1207 * @param eon value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1208 */
1209 private void setEon(BigInteger eon) {
1210 if (eon != null && eon.compareTo(BigInteger.ZERO) == 0) {
1211 // Treat ZERO as field being undefined.
1212 this.eon = null;
1213 } else {
1214 this.eon = eon;
1215 }
1671
1672 if (Qfield == null) {
1673 Qfield = DECIMAL_ZERO;
1674 }
1675
1676 return Pfield.compareTo(Qfield);
1677 }
1678
1679 /**
1680 * <p>Indicates whether parameter <code>obj</code> is "equal to" this one.</p>
1681 *
1682 * @param obj to compare.
1683 *
1684 * @return <code>true</code> when <code>compare(this,(XMLGregorianCalendar)obj) == EQUAL.</code>.
1685 */
1686 public boolean equals(Object obj) {
1687
1688 if (obj == null || !(obj instanceof XMLGregorianCalendar)) {
1689 return false;
1690 }
1691 return compare((XMLGregorianCalendar) obj) == DatatypeConstants.EQUAL;
1692 }
1693
1694 /**
1695 * <p>Returns a hash code consistent with the definition of the equals method.</p>
1696 *
1697 * @return hash code of this object.
1698 */
1699 public int hashCode() {
1700
1701 // Following two dates compare to EQUALS since in different timezones.
1702 // 2000-01-15T12:00:00-05:00 == 2000-01-15T13:00:00-04:00
1703 //
1704 // Must ensure both instances generate same hashcode by normalizing
1705 // this to UTC timezone.
1706 int timezone = getTimezone();
1707 if (timezone == DatatypeConstants.FIELD_UNDEFINED) {
1708 timezone = 0;
1709 }
1710 XMLGregorianCalendar gc = this;
1933 default:
1934 throw new IllegalStateException(
1935 this.getClass().getName()
1936 + "#getXMLSchemaType() :"
1937 + DatatypeMessageFormatter.formatMessage(null, "InvalidXGCFields", null)
1938 );
1939 }
1940 }
1941
1942
1943 /**
1944 * Validate instance by <code>getXMLSchemaType()</code> constraints.
1945 * @return true if data values are valid.
1946 */
1947 public boolean isValid() {
1948 // since setters do not allow for invalid values,
1949 // (except for exceptional case of year field of zero),
1950 // no need to check for anything except for constraints
1951 // between fields.
1952
1953 //check if days in month is valid. Can be dependent on leap year.
1954 if (getMonth() == DatatypeConstants.FEBRUARY) {
1955 // years could not be set
1956 int maxDays = 29;
1957
1958 if (eon == null) {
1959 if(year!=DatatypeConstants.FIELD_UNDEFINED)
1960 maxDays = maximumDayInMonthFor(year,getMonth());
1961 } else {
1962 BigInteger years = getEonAndYear();
1963 if (years != null) {
1964 maxDays = maximumDayInMonthFor(getEonAndYear(), DatatypeConstants.FEBRUARY);
1965 }
1966 }
1967 if (getDay() > maxDays) {
1968 return false;
1969 }
1970 }
1971
1972 // http://www.w3.org/2001/05/xmlschema-errata#e2-45
1973 if (getHour() == 24) {
1974 if(getMinute() != 0) {
1975 return false;
1976 } else if (getSecond() != 0) {
1977 return false;
1978 }
1979 }
1980
1981 // XML Schema 1.0 specification defines year value of zero as
1982 // invalid. Allow this class to set year field to zero
1983 // since XML Schema 1.0 errata states that lexical zero will
1984 // be allowed in next version and treated as 1 B.C.E.
1985 if (eon == null) {
1986 // optimize check.
1987 if (year == 0) {
1988 return false;
1989 }
1990 } else {
1991 BigInteger yearField = getEonAndYear();
1992 if (yearField != null) {
1993 int result = compareField(yearField, BigInteger.ZERO);
1994 if (result == DatatypeConstants.EQUAL) {
1995 return false;
1996 }
1997 }
1998 }
1999 return true;
2000 }
2001
2002 /**
2003 * <p>Add <code>duration</code> to this instance.<\p>
2004 *
2005 * <p>The computation is specified in
2006 * <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
2007 * <i>Adding durations to dateTimes</i>></a>.
2008 * <a href="#datetimefieldsmapping">date/time field mapping table</a>
2009 * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
2010 * to this class' representation of those fields.</p>
2011 *
2012 * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
2013 *
2014 * @throws NullPointerException when <code>duration</code> parameter is <code>null</code>.
2015 */
2016 public void add(Duration duration) {
2017
2018 /*
2196 if (month >= 2) {
2197 mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth() - 1));
2198 } else {
2199 // roll over to December of previous year
2200 mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear().subtract(BigInteger.valueOf((long) 1)), 12));
2201 }
2202 endDays = endDays.add(mdimf);
2203 monthCarry = -1;
2204 } else if (endDays.compareTo(BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
2205 endDays = endDays.add(BigInteger.valueOf(-maximumDayInMonthFor(getEonAndYear(), getMonth())));
2206 monthCarry = 1;
2207 } else {
2208 break;
2209 }
2210
2211 intTemp = getMonth() + monthCarry;
2212 int endMonth = (intTemp - 1) % (13 - 1);
2213 int quotient;
2214 if (endMonth < 0) {
2215 endMonth = (13 - 1) + endMonth + 1;
2216 quotient = new BigDecimal(intTemp - 1).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_UP).intValue();
2217 } else {
2218 quotient = (intTemp - 1) / (13 - 1);
2219 endMonth += 1;
2220 }
2221 setMonth(endMonth);
2222 if (quotient != 0) {
2223 setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
2224 }
2225 }
2226 setDay(endDays.intValue());
2227
2228 // set fields that where undefined before this addition, back to undefined.
2229 for (int i = YEAR; i <= SECOND; i++) {
2230 if (fieldUndefined[i]) {
2231 switch (i) {
2232 case YEAR:
2233 setYear(DatatypeConstants.FIELD_UNDEFINED);
2234 break;
2235 case MONTH:
2236 setMonth(DatatypeConstants.FIELD_UNDEFINED);
2242 setHour(DatatypeConstants.FIELD_UNDEFINED, false);
2243 break;
2244 case MINUTE:
2245 setMinute(DatatypeConstants.FIELD_UNDEFINED);
2246 break;
2247 case SECOND:
2248 setSecond(DatatypeConstants.FIELD_UNDEFINED);
2249 setFractionalSecond(null);
2250 break;
2251 }
2252 }
2253 }
2254 }
2255
2256 private static final BigInteger FOUR = BigInteger.valueOf(4);
2257 private static final BigInteger HUNDRED = BigInteger.valueOf(100);
2258 private static final BigInteger FOUR_HUNDRED = BigInteger.valueOf(400);
2259 private static final BigInteger SIXTY = BigInteger.valueOf(60);
2260 private static final BigInteger TWENTY_FOUR = BigInteger.valueOf(24);
2261 private static final BigInteger TWELVE = BigInteger.valueOf(12);
2262 private static final BigDecimal DECIMAL_ZERO = new BigDecimal("0");
2263 private static final BigDecimal DECIMAL_ONE = new BigDecimal("1");
2264 private static final BigDecimal DECIMAL_SIXTY = new BigDecimal("60");
2265
2266
2267 private static int daysInMonth[] = { 0, // XML Schema months start at 1.
2268 31, 28, 31, 30, 31, 30,
2269 31, 31, 30, 31, 30, 31};
2270
2271 private static int maximumDayInMonthFor(BigInteger year, int month) {
2272 if (month != DatatypeConstants.FEBRUARY) {
2273 return daysInMonth[month];
2274 } else {
2275 if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
2276 (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
2277 year.mod(FOUR).equals(BigInteger.ZERO))) {
2278 // is a leap year.
2279 return 29;
2280 } else {
2281 return daysInMonth[month];
2282 }
2283 }
2284 }
2285
2286 private static int maximumDayInMonthFor(int year, int month) {
2287 if (month != DatatypeConstants.FEBRUARY) {
2288 return daysInMonth[month];
2289 } else {
2290 if (((year % 400) == 0) ||
2291 (((year % 100) != 0) && ((year % 4) == 0))) {
2292 // is a leap year.
2293 return 29;
2294 } else {
2295 return daysInMonth[DatatypeConstants.FEBRUARY];
2296 }
2297 }
2298 }
2299
2300 /**
2301 * <p>Convert <code>this</code> to <code>java.util.GregorianCalendar</code>.</p>
2302 *
2303 * <p>When <code>this</code> instance has an undefined field, this
2304 * conversion relies on the <code>java.util.GregorianCalendar</code> default
2305 * for its corresponding field. A notable difference between
2306 * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
2307 * is that Timezone value is optional for date/time datatypes and it is
2308 * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
2309 * for <code>java.util.TimeZone.getDefault()</code> on how the default
2310 * is determined. To explicitly specify the <code>TimeZone</code>
2311 * instance, see
2312 * {@link #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)}.</p>
2313 *
2314 * <table border="2" rules="all" cellpadding="2">
2315 * <thead>
2387 * </ul>
2388 * </p>
2389 *
2390 * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, XMLGregorianCalendar)
2391 */
2392 public java.util.GregorianCalendar toGregorianCalendar() {
2393
2394 GregorianCalendar result = null;
2395 final int DEFAULT_TIMEZONE_OFFSET = DatatypeConstants.FIELD_UNDEFINED;
2396 TimeZone tz = getTimeZone(DEFAULT_TIMEZONE_OFFSET);
2397 /** Use the following instead for JDK7 only:
2398 * Locale locale = Locale.getDefault(Locale.Category.FORMAT);
2399 */
2400 Locale locale = getDefaultLocale();
2401
2402 result = new GregorianCalendar(tz, locale);
2403 result.clear();
2404 result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2405
2406 // if year( and eon) are undefined, leave default Calendar values
2407 BigInteger year = getEonAndYear();
2408 if (year != null) {
2409 result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2410 result.set(Calendar.YEAR, year.abs().intValue());
2411 }
2412
2413 // only set month if it is set
2414 if (month != DatatypeConstants.FIELD_UNDEFINED) {
2415 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2416 result.set(Calendar.MONTH, month - 1);
2417 }
2418
2419 // only set day if it is set
2420 if (day != DatatypeConstants.FIELD_UNDEFINED) {
2421 result.set(Calendar.DAY_OF_MONTH, day);
2422 }
2423
2424 // only set hour if it is set
2425 if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2426 result.set(Calendar.HOUR_OF_DAY, hour);
2427 }
2428
2429 // only set minute if it is set
2430 if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2431 result.set(Calendar.MINUTE, minute);
2526 public GregorianCalendar toGregorianCalendar(TimeZone timezone,
2527 Locale aLocale,
2528 XMLGregorianCalendar defaults) {
2529 GregorianCalendar result = null;
2530 TimeZone tz = timezone;
2531 if (tz == null) {
2532 int defaultZoneoffset = DatatypeConstants.FIELD_UNDEFINED;
2533 if (defaults != null) {
2534 defaultZoneoffset = defaults.getTimezone();
2535 }
2536 tz = getTimeZone(defaultZoneoffset);
2537 }
2538 if (aLocale == null) {
2539 aLocale = Locale.getDefault();
2540 }
2541 result = new GregorianCalendar(tz, aLocale);
2542 result.clear();
2543 result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2544
2545 // if year( and eon) are undefined, leave default Calendar values
2546 BigInteger year = getEonAndYear();
2547 if (year != null) {
2548 result.set(Calendar.ERA, year.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2549 result.set(Calendar.YEAR, year.abs().intValue());
2550 } else {
2551 // use default if set
2552 BigInteger defaultYear = (defaults != null) ? defaults.getEonAndYear() : null;
2553 if (defaultYear != null) {
2554 result.set(Calendar.ERA, defaultYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2555 result.set(Calendar.YEAR, defaultYear.abs().intValue());
2556 }
2557 }
2558
2559 // only set month if it is set
2560 if (month != DatatypeConstants.FIELD_UNDEFINED) {
2561 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2562 result.set(Calendar.MONTH, month - 1);
2563 } else {
2564 // use default if set
2565 int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
2566 if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
2567 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2568 result.set(Calendar.MONTH, defaultMonth - 1);
2569 }
2570 }
2571
2572 // only set day if it is set
2573 if (day != DatatypeConstants.FIELD_UNDEFINED) {
2574 result.set(Calendar.DAY_OF_MONTH, day);
2575 } else {
2576 // use default if set
2577 int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
2578 if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
2579 result.set(Calendar.DAY_OF_MONTH, defaultDay);
2580 }
2581 }
2582
2583 // only set hour if it is set
2584 if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2585 result.set(Calendar.HOUR_OF_DAY, hour);
2586 } else {
2587 // use default if set
2588 int defaultHour = (defaults != null) ? defaults.getHour() : DatatypeConstants.FIELD_UNDEFINED;
2589 if (defaultHour != DatatypeConstants.FIELD_UNDEFINED) {
2590 result.set(Calendar.HOUR_OF_DAY, defaultHour);
2591 }
2592 }
2593
2594 // only set minute if it is set
2595 if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2596 result.set(Calendar.MINUTE, minute);
2597 } else {
2598 // use default if set
2599 int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
2600 if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
2601 result.set(Calendar.MINUTE, defaultMinute);
2602 }
2603 }
2604
2605 // only set second if it is set
2606 if (second != DatatypeConstants.FIELD_UNDEFINED) {
2607 result.set(Calendar.SECOND, second);
2608 } else {
2609 // use default if set
2610 int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
2611 if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
2612 result.set(Calendar.SECOND, defaultSecond);
2613 }
2614 }
2615
2616 // only set millisend if it is set
2617 if (fractionalSecond != null) {
2618 result.set(Calendar.MILLISECOND, getMillisecond());
2619 } else {
2620 // use default if set
2621 BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
2622 if (defaultFractionalSecond != null) {
2623 result.set(Calendar.MILLISECOND, defaults.getMillisecond());
2624 }
2625 }
2626
2627 return result;
2628 }
2629
2630 /**
2631 * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
2632 *
2633 * <p>If timezone field is defined for this instance,
2634 * returns TimeZone initialized with custom timezone id
2635 * of zoneoffset. If timezone field is undefined,
2636 * try the defaultZoneoffset that was passed in.
2637 * If defaultZoneoffset is DatatypeConstants.FIELD_UNDEFINED, return
2638 * default timezone for this host.
2639 * (Same default as java.util.GregorianCalendar).</p>
2640 *
2641 * @param defaultZoneoffset default zoneoffset if this zoneoffset is
2654 result = TimeZone.getDefault();
2655 } else {
2656 // zoneoffset is in minutes. Convert to custom timezone id format.
2657 char sign = zoneoffset < 0 ? '-' : '+';
2658 if (sign == '-') {
2659 zoneoffset = -zoneoffset;
2660 }
2661 int hour = zoneoffset / 60;
2662 int minutes = zoneoffset - (hour * 60);
2663
2664 // Javadoc for java.util.TimeZone documents max length
2665 // for customTimezoneId is 8 when optional ':' is not used.
2666 // Format is
2667 // "GMT" ('-'|''+') (digit digit?) (digit digit)?
2668 // hour minutes
2669 StringBuffer customTimezoneId = new StringBuffer(8);
2670 customTimezoneId.append("GMT");
2671 customTimezoneId.append(sign);
2672 customTimezoneId.append(hour);
2673 if (minutes != 0) {
2674 customTimezoneId.append(minutes);
2675 }
2676 result = TimeZone.getTimeZone(customTimezoneId.toString());
2677 }
2678 return result;
2679 }
2680
2681 /**
2682 * <p>Creates and returns a copy of this object.</p>
2683 *
2684 * @return copy of this <code>Object</code>
2685 */
2686 public Object clone() {
2687 // Both this.eon and this.fractionalSecond are instances
2688 // of immutable classes, so they do not need to be cloned.
2689 return new XMLGregorianCalendarImpl(getEonAndYear(),
2690 this.month, this.day,
2691 this.hour, this.minute, this.second,
2692 this.fractionalSecond,
2693 this.timezone);
2701 */
2702 public void clear() {
2703 eon = null;
2704 year = DatatypeConstants.FIELD_UNDEFINED;
2705 month = DatatypeConstants.FIELD_UNDEFINED;
2706 day = DatatypeConstants.FIELD_UNDEFINED;
2707 timezone = DatatypeConstants.FIELD_UNDEFINED; // in minutes
2708 hour = DatatypeConstants.FIELD_UNDEFINED;
2709 minute = DatatypeConstants.FIELD_UNDEFINED;
2710 second = DatatypeConstants.FIELD_UNDEFINED;
2711 fractionalSecond = null;
2712 }
2713
2714 public void setMillisecond(int millisecond) {
2715 if (millisecond == DatatypeConstants.FIELD_UNDEFINED) {
2716 fractionalSecond = null;
2717 } else {
2718 if(millisecond<0 || 999<millisecond)
2719 if(millisecond!=DatatypeConstants.FIELD_UNDEFINED)
2720 invalidFieldValue(MILLISECOND, millisecond);
2721 fractionalSecond = new BigDecimal((long) millisecond).movePointLeft(3);
2722 }
2723 }
2724
2725 public void setFractionalSecond(BigDecimal fractional) {
2726 if (fractional != null) {
2727 if ((fractional.compareTo(DECIMAL_ZERO) < 0) ||
2728 (fractional.compareTo(DECIMAL_ONE) > 0)) {
2729 throw new IllegalArgumentException(DatatypeMessageFormatter.formatMessage(null,
2730 "InvalidFractional", new Object[]{fractional}));
2731 }
2732 }
2733 this.fractionalSecond = fractional;
2734 }
2735
2736 private final class Parser {
2737 private final String format;
2738 private final String value;
2739
2740 private final int flen;
2741 private final int vlen;
2753 /**
2754 * <p>Parse a formated <code>String</code> into an <code>XMLGregorianCalendar</code>.</p>
2755 *
2756 * <p>If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value,
2757 * an <code>IllegalArgumentException</code> is thrown.</p>
2758 *
2759 * @throws IllegalArgumentException If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value.
2760 */
2761 public void parse() throws IllegalArgumentException {
2762 while (fidx < flen) {
2763 char fch = format.charAt(fidx++);
2764
2765 if (fch != '%') { // not a meta character
2766 skip(fch);
2767 continue;
2768 }
2769
2770 // seen meta character. we don't do error check against the format
2771 switch (format.charAt(fidx++)) {
2772 case 'Y' : // year
2773 parseAndSetYear(4);
2774 break;
2775
2776 case 'M' : // month
2777 setMonth(parseInt(2, 2));
2778 break;
2779
2780 case 'D' : // days
2781 setDay(parseInt(2, 2));
2782 break;
2783
2784 case 'h' : // hours
2785 setHour(parseInt(2, 2), false);
2786 break;
2787
2788 case 'm' : // minutes
2789 setMinute(parseInt(2, 2));
2790 break;
2791
2792 case 's' : // parse seconds.
2793 setSecond(parseInt(2, 2));
2834
2835 private char read() throws IllegalArgumentException {
2836 if (vidx == vlen) {
2837 throw new IllegalArgumentException(value); //,vidx);
2838 }
2839 return value.charAt(vidx++);
2840 }
2841
2842 private void skip(char ch) throws IllegalArgumentException {
2843 if (read() != ch) {
2844 throw new IllegalArgumentException(value); //,vidx-1);
2845 }
2846 }
2847
2848 private int parseInt(int minDigits, int maxDigits)
2849 throws IllegalArgumentException {
2850
2851 int n = 0;
2852 char ch;
2853 int vstart = vidx;
2854 while (isDigit(ch=peek()) && (vidx - vstart) <= maxDigits) {
2855 vidx++;
2856 n = n*10 + ch-'0';
2857 }
2858 if ((vidx - vstart) < minDigits) {
2859 // we are expecting more digits
2860 throw new IllegalArgumentException(value); //,vidx);
2861 }
2862
2863 return n;
2864 }
2865
2866 private void parseAndSetYear(int minDigits)
2867 throws IllegalArgumentException {
2868 int vstart = vidx;
2869 int n = 0;
2870 boolean neg = false;
2871
2872 // skip leading negative, if it exists
2873 if (peek() == '-') {
2874 vidx++;
2875 neg = true;
2876 }
2877 while(true) {
2878 char ch = peek();
2879 if(!isDigit(ch))
2880 break;
2881 vidx++;
2882 n = n*10 + ch-'0';
2883 }
2884
2885 if ((vidx - vstart) < minDigits) {
2886 // we are expecting more digits
2887 throw new IllegalArgumentException(value); //,vidx);
2888 }
2889
2890 if(vidx-vstart<7) {
2891 // definitely int only. I don't know the exact # of digits that can be in int,
2892 // but as long as we can catch (0-9999) range, that should be enough.
2893 if(neg) n = -n;
2894 year = n;
2895 eon = null;
2896 } else {
2897 setYear(new BigInteger(value.substring(vstart, vidx)));
2898 }
2899 }
2900
2901 private BigDecimal parseBigDecimal()
2902 throws IllegalArgumentException {
2903 int vstart = vidx;
2904
2905 if (peek() == '.') {
2906 vidx++;
2907 } else {
2908 throw new IllegalArgumentException(value);
2909 }
2910 while (isDigit(peek())) {
2911 vidx++;
2912 }
2913 return new BigDecimal(value.substring(vstart, vidx));
2914 }
2915 }
2916
2917 private static boolean isDigit(char ch) {
2918 return '0' <= ch && ch <= '9';
2919 }
2920
2921 /**
2922 * Prints this object according to the format specification.
2923 *
2924 * <p>
2925 * I wrote a custom format method for a particular format string to
2926 * see if it improves the performance, but it didn't. So this interpreting
2927 * approach isn't too bad.
2928 *
2929 * <p>
2930 * StringBuffer -> StringBuilder change had a very visible impact.
2931 * It almost cut the execution time to half, but unfortunately we can't use it
2932 * because we need to run on JDK 1.3
2933 */
2934 private String format( String format ) {
2935 char[] buf = new char[32];
2936 int bufPtr = 0;
2937
2938 int fidx=0,flen=format.length();
2939
2940 while(fidx<flen) {
2941 char fch = format.charAt(fidx++);
2942 if(fch!='%') {// not a meta char
2943 buf[bufPtr++] = fch;
2944 continue;
2945 }
2946
2947 switch(format.charAt(fidx++)) {
2948 case 'Y':
2949 if(eon==null) {
2950 // optimized path
2951 int y = getYear();
2952 if(y<0) {
2953 buf[bufPtr++] = '-';
2954 y = -y;
2955 }
2956 bufPtr = print4Number(buf,bufPtr,y);
2957 } else {
2958 String s = getEonAndYear().toString();
2959 // reallocate the buffer now so that it has enough space
2960 char[] n = new char[buf.length+s.length()];
2961 System.arraycopy(buf,0,n,0,bufPtr);
2962 buf = n;
2963 for(int i=s.length();i<4;i++)
2964 buf[bufPtr++] = '0';
2965 s.getChars(0,s.length(),buf,bufPtr);
2966 bufPtr += s.length();
2967 }
2968 break;
2969 case 'M':
2970 bufPtr = print2Number(buf,bufPtr,getMonth());
2971 break;
2972 case 'D':
2973 bufPtr = print2Number(buf,bufPtr,getDay());
2974 break;
2975 case 'h':
2976 bufPtr = print2Number(buf,bufPtr,getHour());
2977 break;
2978 case 'm':
2979 bufPtr = print2Number(buf,bufPtr,getMinute());
2980 break;
2981 case 's':
2982 bufPtr = print2Number(buf,bufPtr,getSecond());
2983 if (getFractionalSecond() != null) {
2984 // Note: toPlainString() isn't available before Java 1.5
2985 String frac = getFractionalSecond().toString();
2986
2987 int pos = frac.indexOf("E-");
2988 if (pos >= 0) {
2989 String zeros = frac.substring(pos+2);
2990 frac = frac.substring(0,pos);
2991 pos = frac.indexOf(".");
2992 if (pos >= 0) {
2993 frac = frac.substring(0,pos) + frac.substring(pos+1);
2994 }
2995 int count = Integer.parseInt(zeros);
2996 if (count < 40) {
2997 frac = "00000000000000000000000000000000000000000".substring(0,count-1) + frac;
2998 } else {
2999 // do it the hard way
3000 while (count > 1) {
3001 frac = "0" + frac;
3002 count--;
3003 }
3004 }
3005 frac = "0." + frac;
3006 }
3007
3008 // reallocate the buffer now so that it has enough space
3009 char[] n = new char[buf.length+frac.length()];
3010 System.arraycopy(buf,0,n,0,bufPtr);
3011 buf = n;
3012 //skip leading zero.
3013 frac.getChars(1, frac.length(), buf, bufPtr);
3014 bufPtr += frac.length()-1;
3015 }
3016 break;
3017 case 'z':
3018 int offset = getTimezone();
3019 if (offset == 0) {
3020 buf[bufPtr++] = 'Z';
3021 } else
3022 if (offset != DatatypeConstants.FIELD_UNDEFINED) {
3023 if (offset < 0) {
3024 buf[bufPtr++] = '-';
3025 offset *= -1;
3026 } else {
3027 buf[bufPtr++] = '+';
3028 }
3029 bufPtr = print2Number(buf, bufPtr, offset / 60);
3030 buf[bufPtr++] = ':';
3031 bufPtr = print2Number(buf, bufPtr, offset % 60);
3032 }
3033 break;
3034 default:
3035 throw new InternalError(); // impossible
3036 }
3037 }
3038
3039 return new String(buf,0,bufPtr);
3040 }
3041
3042 /**
3043 * Prints an int as two digits into the buffer.
3044 *
3045 * @param number
3046 * Number to be printed. Must be positive.
3047 */
3048 private int print2Number( char[] out, int bufptr, int number ) {
3049 out[bufptr++] = (char) ('0'+(number/10));
3050 out[bufptr++] = (char) ('0'+(number%10));
3051 return bufptr;
3052 }
3053
3054 /**
3055 * Prints an int as four digits into the buffer.
3056 *
3057 * @param number
3058 * Number to be printed. Must be positive.
3059 */
3060 private int print4Number( char[] out, int bufptr, int number ) {
3061 out[bufptr+3] = (char) ('0'+(number%10));
3062 number /= 10;
3063 out[bufptr+2] = (char) ('0'+(number%10));
3064 number /= 10;
3065 out[bufptr+1] = (char) ('0'+(number%10));
3066 number /= 10;
3067 out[bufptr ] = (char) ('0'+(number%10));
3068 return bufptr+4;
3069 }
3070
3071 /**
3072 * Compute <code>value*signum</code> where value==null is treated as
3073 * value==0.
3074 * @return non-null {@link BigInteger}.
3075 */
3076 static BigInteger sanitize(Number value, int signum) {
3077 if (signum == 0 || value == null) {
3078 return BigInteger.ZERO;
3079 }
3080 return (signum < 0)? ((BigInteger)value).negate() : (BigInteger)value;
3081 }
3082
3083 /** <p><code>reset()</code> is designed to allow the reuse of existing
3084 * <code>XMLGregorianCalendar</code>s thus saving resources associated
3085 * with the creation of new <code>XMLGregorianCalendar</code>s.</p>
3086 */
3087 public void reset() {
3088 //PENDING : Implementation of reset method
3089 }
3090 }
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.org.apache.xerces.internal.jaxp.datatype;
27
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.Serializable;
31 import java.math.BigDecimal;
32 import java.math.BigInteger;
33 import java.util.TimeZone;
34 import java.util.Calendar;
35 import java.util.GregorianCalendar;
36 import java.util.Date;
37 import java.util.Locale;
38
39 import javax.xml.datatype.DatatypeConstants;
40 import javax.xml.datatype.Duration;
41 import javax.xml.datatype.XMLGregorianCalendar;
42 import javax.xml.namespace.QName;
43 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
44 import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
45
46 /**
47 * <p>Representation for W3C XML Schema 1.0 date/time datatypes.
48 * Specifically, these date/time datatypes are
49 * {@link DatatypeConstants#DATETIME dateTime},
180 * <li>partial order relation comparator method, {@link #compare(XMLGregorianCalendar)}</li>
181 * <li>{@link #equals(Object)} defined relative to {@link #compare(XMLGregorianCalendar)}.</li>
182 * <li> addition operation with {@link javax.xml.datatype.Duration}.
183 * instance as defined in <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">
184 * W3C XML Schema 1.0 Part 2, Appendix E, <i>Adding durations to dateTimes</i></a>.</li>
185 * </ul>
186 * </p>
187 *
188 * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
189 * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
190 * @author <a href="mailto:Sunitha.Reddy@Sun.com">Sunitha Reddy</a>
191 * @version $Revision: 1.14 $, $Date: 2010-11-10 07:41:41 $
192 * @see javax.xml.datatype.Duration
193 * @since 1.5
194 */
195
196 public class XMLGregorianCalendarImpl
197 extends XMLGregorianCalendar
198 implements Serializable, Cloneable {
199
200 /** Backup values **/
201 transient private BigInteger orig_eon;
202 transient private int orig_year = DatatypeConstants.FIELD_UNDEFINED;
203 transient private int orig_month = DatatypeConstants.FIELD_UNDEFINED;
204 transient private int orig_day = DatatypeConstants.FIELD_UNDEFINED;
205 transient private int orig_hour = DatatypeConstants.FIELD_UNDEFINED;
206 transient private int orig_minute = DatatypeConstants.FIELD_UNDEFINED;
207 transient private int orig_second = DatatypeConstants.FIELD_UNDEFINED;
208 transient private BigDecimal orig_fracSeconds;
209 transient private int orig_timezone = DatatypeConstants.FIELD_UNDEFINED;
210
211 /**
212 * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
213 */
214 private BigInteger eon = null;
215
216 /**
217 * <p>Year of this <code>XMLGregorianCalendar</code>.</p>
218 */
219 private int year = DatatypeConstants.FIELD_UNDEFINED;
220
221 /**
222 * <p>Month of this <code>XMLGregorianCalendar</code>.</p>
223 */
224 private int month = DatatypeConstants.FIELD_UNDEFINED;
225
226 /**
227 * <p>Day of this <code>XMLGregorianCalendar</code>.</p>
228 */
229 private int day = DatatypeConstants.FIELD_UNDEFINED;
230
237 * <p>Hour of this <code>XMLGregorianCalendar</code>.</p>
238 */
239 private int hour = DatatypeConstants.FIELD_UNDEFINED;
240
241 /**
242 * <p>Minute of this <code>XMLGregorianCalendar</code>.</p>
243 */
244 private int minute = DatatypeConstants.FIELD_UNDEFINED;
245
246 /**
247 * <p>Second of this <code>XMLGregorianCalendar</code>.</p>
248 */
249 private int second = DatatypeConstants.FIELD_UNDEFINED ;
250
251 /**
252 * <p>Fractional second of this <code>XMLGregorianCalendar</code>.</p>
253 */
254 private BigDecimal fractionalSecond = null;
255
256 /**
257 * <p>BigInteger constant; representing a billion.</p>
258 */
259 private static final BigInteger BILLION_B = new BigInteger("1000000000");
260
261 /**
262 * <p>int constant; representing a billion.</p>
263 */
264 private static final int BILLION_I = 1000000000;
265
266 /**
267 * <p>Obtain a pure Gregorian Calendar by calling
268 * GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE). </p>
269 */
270 private static final Date PURE_GREGORIAN_CHANGE =
271 new Date(Long.MIN_VALUE);
272
273 /**
274 * Year index for MIN_ and MAX_FIELD_VALUES.
275 */
276 private static final int YEAR = 0;
277
278 /**
279 * Month index for MIN_ and MAX_FIELD_VALUES.
280 */
281 private static final int MONTH = 1;
282
283 /**
284 * Day index for MIN_ and MAX_FIELD_VALUES.
285 */
286 private static final int DAY = 2;
442 format = "%Y" + "%z";
443 } else if (countSeparator == 1) {
444 // GYearMonth
445 format = "%Y-%M" + "%z";
446 } else {
447 // Date or invalid lexicalRepresentation
448 // Fix 4971612: invalid SCCS macro substitution in data string
449 format = "%Y-%M-%D" + "%z";
450 }
451 }
452 Parser p = new Parser(format, lexRep);
453 p.parse();
454
455 // check for validity
456 if (!isValid()) {
457 throw new IllegalArgumentException(
458 DatatypeMessageFormatter.formatMessage(null, "InvalidXGCRepresentation", new Object[]{lexicalRepresentation})
459 //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
460 );
461 }
462
463 save();
464 }
465
466 /**
467 * save original values
468 */
469 private void save() {
470 orig_eon = eon;
471 orig_year = year;
472 orig_month = month;
473 orig_day = day;
474 orig_hour = hour;
475 orig_minute = minute;
476 orig_second = second;
477 orig_fracSeconds = fractionalSecond;
478 orig_timezone = timezone;
479 }
480
481 /**
482 * <p>Create an instance with all date/time datatype fields set to
483 * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.</p>
484 */
485 public XMLGregorianCalendarImpl() {
486
487 // field initializers already do the correct initialization.
488 }
489
490 /**
491 * <p>Private constructor allowing for complete value spaces allowed by
492 * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
493 * builtin datatypes. Note that <code>year</code> parameter supports
494 * arbitrarily large numbers and fractionalSecond has infinite
495 * precision.</p>
496 *
497 * @param year of <code>XMLGregorianCalendar</code> to be created.
498 * @param month of <code>XMLGregorianCalendar</code> to be created.
499 * @param day of <code>XMLGregorianCalendar</code> to be created.
500 * @param hour of <code>XMLGregorianCalendar</code> to be created.
501 * @param minute of <code>XMLGregorianCalendar</code> to be created.
539 String fractionalSecondString = "null";
540 if (fractionalSecond != null) {
541 fractionalSecondString = fractionalSecond.toString();
542 }
543
544 throw new IllegalArgumentException(
545 "year = " + yearString
546 + ", month = " + month
547 + ", day = " + day
548 + ", hour = " + hour
549 + ", minute = " + minute
550 + ", second = " + second
551 + ", fractionalSecond = " + fractionalSecondString
552 + ", timezone = " + timezone
553 + ", is not a valid representation of an XML Gregorian Calendar value."
554 );
555 */
556
557 }
558
559 save();
560 }
561
562 /**
563 * <p>Private constructor of value spaces that a
564 * <code>java.util.GregorianCalendar</code> instance would need to convert to an
565 * <code>XMLGregorianCalendar</code> instance.</p>
566 *
567 * <p><code>XMLGregorianCalendar eon</code> and
568 * <code>fractionalSecond</code> are set to <code>null</code></p>
569 *
570 * @param year of <code>XMLGregorianCalendar</code> to be created.
571 * @param month of <code>XMLGregorianCalendar</code> to be created.
572 * @param day of <code>XMLGregorianCalendar</code> to be created.
573 * @param hour of <code>XMLGregorianCalendar</code> to be created.
574 * @param minute of <code>XMLGregorianCalendar</code> to be created.
575 * @param second of <code>XMLGregorianCalendar</code> to be created.
576 * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
577 * @param timezone of <code>XMLGregorianCalendar</code> to be created.
578 */
579 private XMLGregorianCalendarImpl(
580 int year,
581 int month,
582 int day,
583 int hour,
584 int minute,
585 int second,
586 int millisecond,
587 int timezone) {
588
589 setYear(year);
590 setMonth(month);
591 setDay(day);
592 setTime(hour, minute, second);
593 setTimezone(timezone);
594 BigDecimal realMilliseconds = null;
595 if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
596 realMilliseconds = BigDecimal.valueOf(millisecond, 3);
597 }
598 setFractionalSecond(realMilliseconds);
599
600 if (!isValid()) {
601
602 throw new IllegalArgumentException(
603 DatatypeMessageFormatter.formatMessage(null,
604 "InvalidXGCValue-milli",
605 new Object[] { new Integer(year), new Integer(month), new Integer(day),
606 new Integer(hour), new Integer(minute), new Integer(second),
607 new Integer(millisecond), new Integer(timezone)})
608 );
609 /*
610 throw new IllegalArgumentException(
611 "year = " + year
612 + ", month = " + month
613 + ", day = " + day
614 + ", hour = " + hour
615 + ", minute = " + minute
616 + ", second = " + second
617 + ", millisecond = " + millisecond
618 + ", timezone = " + timezone
619 + ", is not a valid representation of an XML Gregorian Calendar value."
620 );
621 */
622
623 }
624
625 save();
626 }
627
628 /**
629 * <p>Convert a <code>java.util.GregorianCalendar</code> to XML Schema 1.0
630 * representation.</p>
631 *
632 * <table border="2" rules="all" cellpadding="2">
633 * <thead>
634 * <tr>
635 * <th align="center" colspan="2">
636 * Field by Field Conversion from
637 * <code>java.util.GregorianCalendar</code> to this class
638 * </th>
639 * </tr>
640 * </thead>
641 * <tbody>
642 * <tr>
643 * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
644 * <th><code>java.util.GregorianCalendar</code> field</th>
645 * </tr>
686
687 int year = cal.get(Calendar.YEAR);
688 if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
689 year = -year;
690 }
691 this.setYear(year);
692
693 // Calendar.MONTH is zero based, XSD Date datatype's month field starts
694 // with JANUARY as 1.
695 this.setMonth(cal.get(Calendar.MONTH) + 1);
696 this.setDay(cal.get(Calendar.DAY_OF_MONTH));
697 this.setTime(
698 cal.get(Calendar.HOUR_OF_DAY),
699 cal.get(Calendar.MINUTE),
700 cal.get(Calendar.SECOND),
701 cal.get(Calendar.MILLISECOND));
702
703 // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
704 int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
705 this.setTimezone(offsetInMinutes);
706 save();
707 }
708
709 // Factories
710
711 /**
712 * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
713 * All possible fields are specified for this factory method.</p>
714 *
715 * @param year represents both high-order eons and low-order year.
716 * @param month of <code>dateTime</code>
717 * @param day of <code>dateTime</code>
718 * @param hours of <code>dateTime</code>
719 * @param minutes of <code>dateTime</code>
720 * @param seconds of <code>dateTime</code>
721 * @param fractionalSecond value of null indicates optional field is absent.
722 * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
723 *
724 * @return <code>XMLGregorianCalendar</code> created from parameter values.
725 *
726 * @see DatatypeConstants#FIELD_UNDEFINED
1190 }
1191
1192 // setters
1193
1194 /**
1195 * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
1196 *
1197 * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
1198 *
1199 * @param year value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1200 *
1201 * @throws IllegalArgumentException if <code>year</code> parameter is
1202 * outside value constraints for the field as specified in
1203 * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1204 */
1205 public void setYear(BigInteger year) {
1206 if (year == null) {
1207 this.eon = null;
1208 this.year = DatatypeConstants.FIELD_UNDEFINED;
1209 } else {
1210 BigInteger temp = year.remainder(BILLION_B);
1211 this.year = temp.intValue();
1212 setEon(year.subtract(temp));
1213 }
1214 }
1215
1216 /**
1217 * <p>Set year of XSD <code>dateTime</code> year field.</p>
1218 *
1219 * <p>Unset this field by invoking the setter with a parameter value of
1220 * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1221 *
1222 * <p>Note: if the absolute value of the <code>year</code> parameter
1223 * is less than 10^9, the eon component of the XSD year field is set to
1224 * <code>null</code> by this method.</p>
1225 *
1226 * @param year value constraints are summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1227 * If year is {@link DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
1228 */
1229 public void setYear(int year) {
1230 if (year == DatatypeConstants.FIELD_UNDEFINED) {
1231 this.year = DatatypeConstants.FIELD_UNDEFINED;
1232 this.eon = null;
1233 }
1234 else if (Math.abs(year) < BILLION_I) {
1235 this.year = year;
1236 this.eon = null;
1237 } else {
1238 BigInteger theYear = BigInteger.valueOf((long) year);
1239 BigInteger remainder = theYear.remainder(BILLION_B);
1240 this.year = remainder.intValue();
1241 setEon(theYear.subtract(remainder));
1242 }
1243 }
1244
1245 /**
1246 * <p>Set high order part of XSD <code>dateTime</code> year field.</p>
1247 *
1248 * <p>Unset this field by invoking the setter with a parameter value of
1249 * <code>null</code>.</p>
1250 *
1251 * @param eon value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1252 */
1253 private void setEon(BigInteger eon) {
1254 if (eon != null && eon.compareTo(BigInteger.ZERO) == 0) {
1255 // Treat ZERO as field being undefined.
1256 this.eon = null;
1257 } else {
1258 this.eon = eon;
1259 }
1715
1716 if (Qfield == null) {
1717 Qfield = DECIMAL_ZERO;
1718 }
1719
1720 return Pfield.compareTo(Qfield);
1721 }
1722
1723 /**
1724 * <p>Indicates whether parameter <code>obj</code> is "equal to" this one.</p>
1725 *
1726 * @param obj to compare.
1727 *
1728 * @return <code>true</code> when <code>compare(this,(XMLGregorianCalendar)obj) == EQUAL.</code>.
1729 */
1730 public boolean equals(Object obj) {
1731
1732 if (obj == null || !(obj instanceof XMLGregorianCalendar)) {
1733 return false;
1734 }
1735 if (obj == this) {
1736 return true;
1737 }
1738 return compare((XMLGregorianCalendar) obj) == DatatypeConstants.EQUAL;
1739 }
1740
1741 /**
1742 * <p>Returns a hash code consistent with the definition of the equals method.</p>
1743 *
1744 * @return hash code of this object.
1745 */
1746 public int hashCode() {
1747
1748 // Following two dates compare to EQUALS since in different timezones.
1749 // 2000-01-15T12:00:00-05:00 == 2000-01-15T13:00:00-04:00
1750 //
1751 // Must ensure both instances generate same hashcode by normalizing
1752 // this to UTC timezone.
1753 int timezone = getTimezone();
1754 if (timezone == DatatypeConstants.FIELD_UNDEFINED) {
1755 timezone = 0;
1756 }
1757 XMLGregorianCalendar gc = this;
1980 default:
1981 throw new IllegalStateException(
1982 this.getClass().getName()
1983 + "#getXMLSchemaType() :"
1984 + DatatypeMessageFormatter.formatMessage(null, "InvalidXGCFields", null)
1985 );
1986 }
1987 }
1988
1989
1990 /**
1991 * Validate instance by <code>getXMLSchemaType()</code> constraints.
1992 * @return true if data values are valid.
1993 */
1994 public boolean isValid() {
1995 // since setters do not allow for invalid values,
1996 // (except for exceptional case of year field of zero),
1997 // no need to check for anything except for constraints
1998 // between fields.
1999
2000 // check if days in month is valid. Can be dependent on leap year.
2001 if (month != DatatypeConstants.FIELD_UNDEFINED && day != DatatypeConstants.FIELD_UNDEFINED) {
2002 if (year != DatatypeConstants.FIELD_UNDEFINED) {
2003 if (eon == null) {
2004 if (day > maximumDayInMonthFor(year, month)) {
2005 return false;
2006 }
2007 }
2008 else if (day > maximumDayInMonthFor(getEonAndYear(), month)) {
2009 return false;
2010 }
2011 }
2012 // Use 2000 as a default since it's a leap year.
2013 else if (day > maximumDayInMonthFor(2000, month)) {
2014 return false;
2015 }
2016 }
2017
2018 // http://www.w3.org/2001/05/xmlschema-errata#e2-45
2019 if (hour == 24 && (minute != 0 || second != 0 ||
2020 (fractionalSecond != null && fractionalSecond.compareTo(DECIMAL_ZERO) != 0))) {
2021 return false;
2022 }
2023
2024 // XML Schema 1.0 specification defines year value of zero as
2025 // invalid. Allow this class to set year field to zero
2026 // since XML Schema 1.0 errata states that lexical zero will
2027 // be allowed in next version and treated as 1 B.C.E.
2028 if (eon == null && year == 0) {
2029 return false;
2030 }
2031 return true;
2032 }
2033
2034 /**
2035 * <p>Add <code>duration</code> to this instance.<\p>
2036 *
2037 * <p>The computation is specified in
2038 * <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
2039 * <i>Adding durations to dateTimes</i>></a>.
2040 * <a href="#datetimefieldsmapping">date/time field mapping table</a>
2041 * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
2042 * to this class' representation of those fields.</p>
2043 *
2044 * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
2045 *
2046 * @throws NullPointerException when <code>duration</code> parameter is <code>null</code>.
2047 */
2048 public void add(Duration duration) {
2049
2050 /*
2228 if (month >= 2) {
2229 mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth() - 1));
2230 } else {
2231 // roll over to December of previous year
2232 mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear().subtract(BigInteger.valueOf((long) 1)), 12));
2233 }
2234 endDays = endDays.add(mdimf);
2235 monthCarry = -1;
2236 } else if (endDays.compareTo(BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
2237 endDays = endDays.add(BigInteger.valueOf(-maximumDayInMonthFor(getEonAndYear(), getMonth())));
2238 monthCarry = 1;
2239 } else {
2240 break;
2241 }
2242
2243 intTemp = getMonth() + monthCarry;
2244 int endMonth = (intTemp - 1) % (13 - 1);
2245 int quotient;
2246 if (endMonth < 0) {
2247 endMonth = (13 - 1) + endMonth + 1;
2248 quotient = BigDecimal.valueOf(intTemp - 1).divide(new BigDecimal(TWELVE), BigDecimal.ROUND_UP).intValue();
2249 } else {
2250 quotient = (intTemp - 1) / (13 - 1);
2251 endMonth += 1;
2252 }
2253 setMonth(endMonth);
2254 if (quotient != 0) {
2255 setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
2256 }
2257 }
2258 setDay(endDays.intValue());
2259
2260 // set fields that where undefined before this addition, back to undefined.
2261 for (int i = YEAR; i <= SECOND; i++) {
2262 if (fieldUndefined[i]) {
2263 switch (i) {
2264 case YEAR:
2265 setYear(DatatypeConstants.FIELD_UNDEFINED);
2266 break;
2267 case MONTH:
2268 setMonth(DatatypeConstants.FIELD_UNDEFINED);
2274 setHour(DatatypeConstants.FIELD_UNDEFINED, false);
2275 break;
2276 case MINUTE:
2277 setMinute(DatatypeConstants.FIELD_UNDEFINED);
2278 break;
2279 case SECOND:
2280 setSecond(DatatypeConstants.FIELD_UNDEFINED);
2281 setFractionalSecond(null);
2282 break;
2283 }
2284 }
2285 }
2286 }
2287
2288 private static final BigInteger FOUR = BigInteger.valueOf(4);
2289 private static final BigInteger HUNDRED = BigInteger.valueOf(100);
2290 private static final BigInteger FOUR_HUNDRED = BigInteger.valueOf(400);
2291 private static final BigInteger SIXTY = BigInteger.valueOf(60);
2292 private static final BigInteger TWENTY_FOUR = BigInteger.valueOf(24);
2293 private static final BigInteger TWELVE = BigInteger.valueOf(12);
2294 private static final BigDecimal DECIMAL_ZERO = BigDecimal.valueOf(0);
2295 private static final BigDecimal DECIMAL_ONE = BigDecimal.valueOf(1);
2296 private static final BigDecimal DECIMAL_SIXTY = BigDecimal.valueOf(60);
2297
2298
2299 private static class DaysInMonth {
2300 private static final int [] table = { 0, // XML Schema months start at 1.
2301 31, 28, 31, 30, 31, 30,
2302 31, 31, 30, 31, 30, 31};
2303 }
2304
2305 private static int maximumDayInMonthFor(BigInteger year, int month) {
2306 if (month != DatatypeConstants.FEBRUARY) {
2307 return DaysInMonth.table[month];
2308 } else {
2309 if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
2310 (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
2311 year.mod(FOUR).equals(BigInteger.ZERO))) {
2312 // is a leap year.
2313 return 29;
2314 } else {
2315 return DaysInMonth.table[month];
2316 }
2317 }
2318 }
2319
2320 private static int maximumDayInMonthFor(int year, int month) {
2321 if (month != DatatypeConstants.FEBRUARY) {
2322 return DaysInMonth.table[month];
2323 } else {
2324 if (((year % 400) == 0) ||
2325 (((year % 100) != 0) && ((year % 4) == 0))) {
2326 // is a leap year.
2327 return 29;
2328 } else {
2329 return DaysInMonth.table[DatatypeConstants.FEBRUARY];
2330 }
2331 }
2332 }
2333
2334 /**
2335 * <p>Convert <code>this</code> to <code>java.util.GregorianCalendar</code>.</p>
2336 *
2337 * <p>When <code>this</code> instance has an undefined field, this
2338 * conversion relies on the <code>java.util.GregorianCalendar</code> default
2339 * for its corresponding field. A notable difference between
2340 * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
2341 * is that Timezone value is optional for date/time datatypes and it is
2342 * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
2343 * for <code>java.util.TimeZone.getDefault()</code> on how the default
2344 * is determined. To explicitly specify the <code>TimeZone</code>
2345 * instance, see
2346 * {@link #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)}.</p>
2347 *
2348 * <table border="2" rules="all" cellpadding="2">
2349 * <thead>
2421 * </ul>
2422 * </p>
2423 *
2424 * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, XMLGregorianCalendar)
2425 */
2426 public java.util.GregorianCalendar toGregorianCalendar() {
2427
2428 GregorianCalendar result = null;
2429 final int DEFAULT_TIMEZONE_OFFSET = DatatypeConstants.FIELD_UNDEFINED;
2430 TimeZone tz = getTimeZone(DEFAULT_TIMEZONE_OFFSET);
2431 /** Use the following instead for JDK7 only:
2432 * Locale locale = Locale.getDefault(Locale.Category.FORMAT);
2433 */
2434 Locale locale = getDefaultLocale();
2435
2436 result = new GregorianCalendar(tz, locale);
2437 result.clear();
2438 result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2439
2440 // if year( and eon) are undefined, leave default Calendar values
2441 if (year != DatatypeConstants.FIELD_UNDEFINED) {
2442 if (eon == null) {
2443 result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2444 result.set(Calendar.YEAR, Math.abs(year));
2445 }
2446 else {
2447 BigInteger eonAndYear = getEonAndYear();
2448 result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2449 result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2450 }
2451 }
2452
2453 // only set month if it is set
2454 if (month != DatatypeConstants.FIELD_UNDEFINED) {
2455 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2456 result.set(Calendar.MONTH, month - 1);
2457 }
2458
2459 // only set day if it is set
2460 if (day != DatatypeConstants.FIELD_UNDEFINED) {
2461 result.set(Calendar.DAY_OF_MONTH, day);
2462 }
2463
2464 // only set hour if it is set
2465 if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2466 result.set(Calendar.HOUR_OF_DAY, hour);
2467 }
2468
2469 // only set minute if it is set
2470 if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2471 result.set(Calendar.MINUTE, minute);
2566 public GregorianCalendar toGregorianCalendar(TimeZone timezone,
2567 Locale aLocale,
2568 XMLGregorianCalendar defaults) {
2569 GregorianCalendar result = null;
2570 TimeZone tz = timezone;
2571 if (tz == null) {
2572 int defaultZoneoffset = DatatypeConstants.FIELD_UNDEFINED;
2573 if (defaults != null) {
2574 defaultZoneoffset = defaults.getTimezone();
2575 }
2576 tz = getTimeZone(defaultZoneoffset);
2577 }
2578 if (aLocale == null) {
2579 aLocale = Locale.getDefault();
2580 }
2581 result = new GregorianCalendar(tz, aLocale);
2582 result.clear();
2583 result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2584
2585 // if year( and eon) are undefined, leave default Calendar values
2586 if (year != DatatypeConstants.FIELD_UNDEFINED) {
2587 if (eon == null) {
2588 result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2589 result.set(Calendar.YEAR, Math.abs(year));
2590 }
2591 else {
2592 final BigInteger eonAndYear = getEonAndYear();
2593 result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2594 result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2595 }
2596 } else {
2597 // use default if set
2598 if (defaults != null) {
2599 final int defaultYear = defaults.getYear();
2600 if (defaultYear != DatatypeConstants.FIELD_UNDEFINED) {
2601 if (defaults.getEon() == null) {
2602 result.set(Calendar.ERA, defaultYear < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2603 result.set(Calendar.YEAR, Math.abs(defaultYear));
2604 }
2605 else {
2606 final BigInteger defaultEonAndYear = defaults.getEonAndYear();
2607 result.set(Calendar.ERA, defaultEonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2608 result.set(Calendar.YEAR, defaultEonAndYear.abs().intValue());
2609 }
2610 }
2611 }
2612 }
2613
2614 // only set month if it is set
2615 if (month != DatatypeConstants.FIELD_UNDEFINED) {
2616 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2617 result.set(Calendar.MONTH, month - 1);
2618 } else {
2619 // use default if set
2620 final int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
2621 if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
2622 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2623 result.set(Calendar.MONTH, defaultMonth - 1);
2624 }
2625 }
2626
2627 // only set day if it is set
2628 if (day != DatatypeConstants.FIELD_UNDEFINED) {
2629 result.set(Calendar.DAY_OF_MONTH, day);
2630 } else {
2631 // use default if set
2632 final int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
2633 if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
2634 result.set(Calendar.DAY_OF_MONTH, defaultDay);
2635 }
2636 }
2637
2638 // only set hour if it is set
2639 if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2640 result.set(Calendar.HOUR_OF_DAY, hour);
2641 } else {
2642 // use default if set
2643 int defaultHour = (defaults != null) ? defaults.getHour() : DatatypeConstants.FIELD_UNDEFINED;
2644 if (defaultHour != DatatypeConstants.FIELD_UNDEFINED) {
2645 result.set(Calendar.HOUR_OF_DAY, defaultHour);
2646 }
2647 }
2648
2649 // only set minute if it is set
2650 if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2651 result.set(Calendar.MINUTE, minute);
2652 } else {
2653 // use default if set
2654 final int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
2655 if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
2656 result.set(Calendar.MINUTE, defaultMinute);
2657 }
2658 }
2659
2660 // only set second if it is set
2661 if (second != DatatypeConstants.FIELD_UNDEFINED) {
2662 result.set(Calendar.SECOND, second);
2663 } else {
2664 // use default if set
2665 final int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
2666 if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
2667 result.set(Calendar.SECOND, defaultSecond);
2668 }
2669 }
2670
2671 // only set millisend if it is set
2672 if (fractionalSecond != null) {
2673 result.set(Calendar.MILLISECOND, getMillisecond());
2674 } else {
2675 // use default if set
2676 final BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
2677 if (defaultFractionalSecond != null) {
2678 result.set(Calendar.MILLISECOND, defaults.getMillisecond());
2679 }
2680 }
2681
2682 return result;
2683 }
2684
2685 /**
2686 * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
2687 *
2688 * <p>If timezone field is defined for this instance,
2689 * returns TimeZone initialized with custom timezone id
2690 * of zoneoffset. If timezone field is undefined,
2691 * try the defaultZoneoffset that was passed in.
2692 * If defaultZoneoffset is DatatypeConstants.FIELD_UNDEFINED, return
2693 * default timezone for this host.
2694 * (Same default as java.util.GregorianCalendar).</p>
2695 *
2696 * @param defaultZoneoffset default zoneoffset if this zoneoffset is
2709 result = TimeZone.getDefault();
2710 } else {
2711 // zoneoffset is in minutes. Convert to custom timezone id format.
2712 char sign = zoneoffset < 0 ? '-' : '+';
2713 if (sign == '-') {
2714 zoneoffset = -zoneoffset;
2715 }
2716 int hour = zoneoffset / 60;
2717 int minutes = zoneoffset - (hour * 60);
2718
2719 // Javadoc for java.util.TimeZone documents max length
2720 // for customTimezoneId is 8 when optional ':' is not used.
2721 // Format is
2722 // "GMT" ('-'|''+') (digit digit?) (digit digit)?
2723 // hour minutes
2724 StringBuffer customTimezoneId = new StringBuffer(8);
2725 customTimezoneId.append("GMT");
2726 customTimezoneId.append(sign);
2727 customTimezoneId.append(hour);
2728 if (minutes != 0) {
2729 if (minutes < 10) {
2730 customTimezoneId.append('0');
2731 }
2732 customTimezoneId.append(minutes);
2733 }
2734 result = TimeZone.getTimeZone(customTimezoneId.toString());
2735 }
2736 return result;
2737 }
2738
2739 /**
2740 * <p>Creates and returns a copy of this object.</p>
2741 *
2742 * @return copy of this <code>Object</code>
2743 */
2744 public Object clone() {
2745 // Both this.eon and this.fractionalSecond are instances
2746 // of immutable classes, so they do not need to be cloned.
2747 return new XMLGregorianCalendarImpl(getEonAndYear(),
2748 this.month, this.day,
2749 this.hour, this.minute, this.second,
2750 this.fractionalSecond,
2751 this.timezone);
2759 */
2760 public void clear() {
2761 eon = null;
2762 year = DatatypeConstants.FIELD_UNDEFINED;
2763 month = DatatypeConstants.FIELD_UNDEFINED;
2764 day = DatatypeConstants.FIELD_UNDEFINED;
2765 timezone = DatatypeConstants.FIELD_UNDEFINED; // in minutes
2766 hour = DatatypeConstants.FIELD_UNDEFINED;
2767 minute = DatatypeConstants.FIELD_UNDEFINED;
2768 second = DatatypeConstants.FIELD_UNDEFINED;
2769 fractionalSecond = null;
2770 }
2771
2772 public void setMillisecond(int millisecond) {
2773 if (millisecond == DatatypeConstants.FIELD_UNDEFINED) {
2774 fractionalSecond = null;
2775 } else {
2776 if(millisecond<0 || 999<millisecond)
2777 if(millisecond!=DatatypeConstants.FIELD_UNDEFINED)
2778 invalidFieldValue(MILLISECOND, millisecond);
2779 fractionalSecond = BigDecimal.valueOf(millisecond, 3);
2780 }
2781 }
2782
2783 public void setFractionalSecond(BigDecimal fractional) {
2784 if (fractional != null) {
2785 if ((fractional.compareTo(DECIMAL_ZERO) < 0) ||
2786 (fractional.compareTo(DECIMAL_ONE) > 0)) {
2787 throw new IllegalArgumentException(DatatypeMessageFormatter.formatMessage(null,
2788 "InvalidFractional", new Object[]{fractional}));
2789 }
2790 }
2791 this.fractionalSecond = fractional;
2792 }
2793
2794 private final class Parser {
2795 private final String format;
2796 private final String value;
2797
2798 private final int flen;
2799 private final int vlen;
2811 /**
2812 * <p>Parse a formated <code>String</code> into an <code>XMLGregorianCalendar</code>.</p>
2813 *
2814 * <p>If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value,
2815 * an <code>IllegalArgumentException</code> is thrown.</p>
2816 *
2817 * @throws IllegalArgumentException If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value.
2818 */
2819 public void parse() throws IllegalArgumentException {
2820 while (fidx < flen) {
2821 char fch = format.charAt(fidx++);
2822
2823 if (fch != '%') { // not a meta character
2824 skip(fch);
2825 continue;
2826 }
2827
2828 // seen meta character. we don't do error check against the format
2829 switch (format.charAt(fidx++)) {
2830 case 'Y' : // year
2831 parseYear();
2832 break;
2833
2834 case 'M' : // month
2835 setMonth(parseInt(2, 2));
2836 break;
2837
2838 case 'D' : // days
2839 setDay(parseInt(2, 2));
2840 break;
2841
2842 case 'h' : // hours
2843 setHour(parseInt(2, 2), false);
2844 break;
2845
2846 case 'm' : // minutes
2847 setMinute(parseInt(2, 2));
2848 break;
2849
2850 case 's' : // parse seconds.
2851 setSecond(parseInt(2, 2));
2892
2893 private char read() throws IllegalArgumentException {
2894 if (vidx == vlen) {
2895 throw new IllegalArgumentException(value); //,vidx);
2896 }
2897 return value.charAt(vidx++);
2898 }
2899
2900 private void skip(char ch) throws IllegalArgumentException {
2901 if (read() != ch) {
2902 throw new IllegalArgumentException(value); //,vidx-1);
2903 }
2904 }
2905
2906 private int parseInt(int minDigits, int maxDigits)
2907 throws IllegalArgumentException {
2908
2909 int n = 0;
2910 char ch;
2911 int vstart = vidx;
2912 while (isDigit(ch=peek()) && (vidx - vstart) < maxDigits) {
2913 vidx++;
2914 n = n*10 + ch-'0';
2915 }
2916 if ((vidx - vstart) < minDigits) {
2917 // we are expecting more digits
2918 throw new IllegalArgumentException(value); //,vidx);
2919 }
2920
2921 return n;
2922 }
2923
2924 private void parseYear()
2925 throws IllegalArgumentException {
2926 int vstart = vidx;
2927 int sign = 0;
2928
2929 // skip leading negative, if it exists
2930 if (peek() == '-') {
2931 vidx++;
2932 sign = 1;
2933 }
2934 while (isDigit(peek())) {
2935 vidx++;
2936 }
2937 final int digits = vidx - vstart - sign;
2938 if (digits < 4) {
2939 // we are expecting more digits
2940 throw new IllegalArgumentException(value); //,vidx);
2941 }
2942 final String yearString = value.substring(vstart, vidx);
2943 if (digits < 10) {
2944 setYear(Integer.parseInt(yearString));
2945 }
2946 else {
2947 setYear(new BigInteger(yearString));
2948 }
2949 }
2950
2951 private BigDecimal parseBigDecimal()
2952 throws IllegalArgumentException {
2953 int vstart = vidx;
2954
2955 if (peek() == '.') {
2956 vidx++;
2957 } else {
2958 throw new IllegalArgumentException(value);
2959 }
2960 while (isDigit(peek())) {
2961 vidx++;
2962 }
2963 return new BigDecimal(value.substring(vstart, vidx));
2964 }
2965 }
2966
2967 private static boolean isDigit(char ch) {
2968 return '0' <= ch && ch <= '9';
2969 }
2970
2971 /**
2972 * Prints this object according to the format specification.
2973 *
2974 * <p>
2975 * StringBuffer -> StringBuilder change had a very visible impact.
2976 * It almost cut the execution time to half.
2977 * Diff from Xerces:
2978 * Xerces use StringBuffer due to the requirement to support
2979 * JDKs older than JDK 1.5
2980 */
2981 private String format( String format ) {
2982 StringBuilder buf = new StringBuilder();
2983 int fidx=0,flen=format.length();
2984
2985 while(fidx<flen) {
2986 char fch = format.charAt(fidx++);
2987 if(fch!='%') {// not a meta char
2988 buf.append(fch);
2989 continue;
2990 }
2991
2992 switch(format.charAt(fidx++)) {
2993 case 'Y':
2994 if (eon == null) {
2995 int absYear = year;
2996 if (absYear < 0) {
2997 buf.append('-');
2998 absYear = -year;
2999 }
3000 printNumber(buf, absYear, 4);
3001 }
3002 else {
3003 printNumber(buf, getEonAndYear(), 4);
3004 }
3005 break;
3006 case 'M':
3007 printNumber(buf,getMonth(),2);
3008 break;
3009 case 'D':
3010 printNumber(buf,getDay(),2);
3011 break;
3012 case 'h':
3013 printNumber(buf,getHour(),2);
3014 break;
3015 case 'm':
3016 printNumber(buf,getMinute(),2);
3017 break;
3018 case 's':
3019 printNumber(buf,getSecond(),2);
3020 if (getFractionalSecond() != null) {
3021 //Xerces uses a custom method toString instead of
3022 //toPlainString() since it needs to support JDKs older than 1.5
3023 String frac = getFractionalSecond().toPlainString();
3024 //skip leading zero.
3025 buf.append(frac.substring(1, frac.length()));
3026 }
3027 break;
3028 case 'z':
3029 int offset = getTimezone();
3030 if (offset == 0) {
3031 buf.append('Z');
3032 }
3033 else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
3034 if (offset < 0) {
3035 buf.append('-');
3036 offset *= -1;
3037 }
3038 else {
3039 buf.append('+');
3040 }
3041 printNumber(buf,offset/60,2);
3042 buf.append(':');
3043 printNumber(buf,offset%60,2);
3044 }
3045 break;
3046 default:
3047 throw new InternalError(); // impossible
3048 }
3049 }
3050
3051 return buf.toString();
3052 }
3053
3054 /**
3055 * Prints an integer as a String.
3056 *
3057 * @param out
3058 * The formatted string will be appended into this buffer.
3059 * @param number
3060 * The integer to be printed.
3061 * @param nDigits
3062 * The field will be printed by using at least this
3063 * number of digits. For example, 5 will be printed as "0005"
3064 * if nDigits==4.
3065 */
3066 private void printNumber( StringBuilder out, int number, int nDigits ) {
3067 String s = String.valueOf(number);
3068 for (int i = s.length(); i < nDigits; i++) {
3069 out.append('0');
3070 }
3071 out.append(s);
3072 }
3073
3074 /**
3075 * Prints an BigInteger as a String.
3076 *
3077 * @param out
3078 * The formatted string will be appended into this buffer.
3079 * @param number
3080 * The integer to be printed.
3081 * @param nDigits
3082 * The field will be printed by using at least this
3083 * number of digits. For example, 5 will be printed as "0005"
3084 * if nDigits==4.
3085 */
3086 private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
3087 String s = number.toString();
3088 for (int i=s.length(); i < nDigits; i++) {
3089 out.append('0');
3090 }
3091 out.append(s);
3092 }
3093
3094 /**
3095 * Compute <code>value*signum</code> where value==null is treated as
3096 * value==0.
3097 * @return non-null {@link BigInteger}.
3098 */
3099 static BigInteger sanitize(Number value, int signum) {
3100 if (signum == 0 || value == null) {
3101 return BigInteger.ZERO;
3102 }
3103 return (signum < 0)? ((BigInteger)value).negate() : (BigInteger)value;
3104 }
3105
3106 /** <p><code>reset()</code> is designed to allow the reuse of existing
3107 * <code>XMLGregorianCalendar</code>s thus saving resources associated
3108 * with the creation of new <code>XMLGregorianCalendar</code>s.</p>
3109 */
3110 public void reset() {
3111 eon = orig_eon;
3112 year = orig_year;
3113 month = orig_month;
3114 day = orig_day;
3115 hour = orig_hour;
3116 minute = orig_minute;
3117 second = orig_second;
3118 fractionalSecond = orig_fracSeconds;
3119 timezone = orig_timezone;
3120 }
3121
3122 /** Deserialize Calendar. */
3123 private void readObject(ObjectInputStream ois)
3124 throws ClassNotFoundException, IOException {
3125
3126 // perform default deseralization
3127 ois.defaultReadObject();
3128
3129 // initialize orig_* fields
3130 save();
3131
3132 } // readObject(ObjectInputStream)
3133 }
|