KickJava   Java API By Example, From Geeks To Geeks.

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


1 // $Id: DurationImpl.java,v 1.8 2004/06/25 10:09:34 nb131165 Exp $
2

3 /*
4  * @(#)DurationImpl.java 1.6 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.IOException JavaDoc;
13 import java.io.ObjectStreamException JavaDoc;
14 import java.io.Serializable JavaDoc;
15 import java.math.BigDecimal JavaDoc;
16 import java.math.BigInteger JavaDoc;
17 import java.util.Calendar JavaDoc;
18 import java.util.Date JavaDoc;
19 import java.util.GregorianCalendar JavaDoc;
20
21 import javax.xml.datatype.DatatypeConstants JavaDoc;
22 import javax.xml.datatype.Duration JavaDoc;
23 import javax.xml.datatype.XMLGregorianCalendar JavaDoc;
24 import javax.xml.namespace.QName JavaDoc;
25 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
26
27 /**
28  * <p>Immutable representation of a time span as defined in
29  * the W3C XML Schema 1.0 specification.</p>
30  *
31  * <p>A Duration object represents a period of Gregorian time,
32  * which consists of six fields (years, months, days, hours,
33  * minutes, and seconds) plus a sign (+/-) field.</p>
34  *
35  * <p>The first five fields have non-negative (>=0) integers or null
36  * (which represents that the field is not set),
37  * and the seconds field has a non-negative decimal or null.
38  * A negative sign indicates a negative duration.</p>
39  *
40  * <p>This class provides a number of methods that make it easy
41  * to use for the duration datatype of XML Schema 1.0 with
42  * the errata.</p>
43  *
44  * <h2>Order relationship</h2>
45  * <p>Duration objects only have partial order, where two values A and B
46  * maybe either:</p>
47  * <ol>
48  * <li>A&lt;B (A is shorter than B)
49  * <li>A&gt;B (A is longer than B)
50  * <li>A==B (A and B are of the same duration)
51  * <li>A&lt;>B (Comparison between A and B is indeterminate)
52  * </ol>
53  * <p>For example, 30 days cannot be meaningfully compared to one month.
54  * The {@link #compare(Duration)} method implements this
55  * relationship.</p>
56  *
57  * <p>See the {@link #isLongerThan(Duration)} method for details about
58  * the order relationship among {@link Duration} objects.</p>
59  *
60  *
61  *
62  * <h2>Operations over Duration</h2>
63  * <p>This class provides a set of basic arithmetic operations, such
64  * as addition, subtraction and multiplication.
65  * Because durations don't have total order, an operation could
66  * fail for some combinations of operations. For example, you cannot
67  * subtract 15 days from 1 month. See the javadoc of those methods
68  * for detailed conditions where this could happen.</p>
69  *
70  * <p>Also, division of a duration by a number is not provided because
71  * the {@link Duration} class can only deal with finite precision
72  * decimal numbers. For example, one cannot represent 1 sec divided by 3.</p>
73  *
74  * <p>However, you could substitute a division by 3 with multiplying
75  * by numbers such as 0.3 or 0.333.</p>
76  *
77  *
78  *
79  * <h2>Range of allowed values</h2>
80  * <p>
81  * Because some operations of {@link Duration} rely on {@link Calendar}
82  * even though {@link Duration} can hold very large or very small values,
83  * some of the methods may not work correctly on such {@link Duration}s.
84  * The impacted methods document their dependency on {@link Calendar}.
85  *
86  *
87  * @author <a HREF="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
88  * @author <a HREF="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
89  * @version $Revision: 1.8 $, $Date: 2004/06/25 10:09:34 $
90
91  * @see XMLGregorianCalendar#add(Duration)
92  * @since 1.5
93  */

94 public class DurationImpl
95     extends Duration JavaDoc
96     implements Serializable JavaDoc {
97     
98     /**
99      * <p>Number of Fields.</p>
100      */

101     private static final int FIELD_NUM = 6;
102     
103     /**
104      * <p>Internal array of value Fields.</p>
105      */

106     private static final DatatypeConstants.Field JavaDoc[] FIELDS = new DatatypeConstants.Field JavaDoc[]{
107             DatatypeConstants.YEARS,
108             DatatypeConstants.MONTHS,
109             DatatypeConstants.DAYS,
110             DatatypeConstants.HOURS,
111             DatatypeConstants.MINUTES,
112             DatatypeConstants.SECONDS
113         };
114
115         /**
116          * <p>Internal array of value Field ids.</p>
117          */

118         private static final int[] FIELD_IDS = {
119                 DatatypeConstants.YEARS.getId(),
120                 DatatypeConstants.MONTHS.getId(),
121                 DatatypeConstants.DAYS.getId(),
122                 DatatypeConstants.HOURS.getId(),
123                 DatatypeConstants.MINUTES.getId(),
124                 DatatypeConstants.SECONDS.getId()
125             };
126
127     /**
128      * <p>BigDecimal value of 0.</p>
129      */

130     private static final BigDecimal JavaDoc ZERO = BigDecimal.valueOf((long) 0);
131
132     /**
133      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
134      * zero, or positive.</p>
135      */

136     private final int signum;
137     
138     /**
139      * <p>Years of this <code>Duration</code>.</p>
140      */

141     private final BigInteger JavaDoc years;
142     
143     /**
144      * <p>Months of this <code>Duration</code>.</p>
145      */

146     private final BigInteger JavaDoc months;
147     
148     /**
149      * <p>Days of this <code>Duration</code>.</p>
150      */

151     private final BigInteger JavaDoc days;
152     
153     /**
154      * <p>Hours of this <code>Duration</code>.</p>
155      */

156     private final BigInteger JavaDoc hours;
157     
158     /**
159      * <p>Minutes of this <code>Duration</code>.</p>
160      */

161     private final BigInteger JavaDoc minutes;
162     
163     /**
164      * <p>Seconds of this <code>Duration</code>.</p>
165      */

166     private final BigDecimal JavaDoc seconds;
167
168     /**
169      * Returns the sign of this duration in -1,0, or 1.
170      *
171      * @return
172      * -1 if this duration is negative, 0 if the duration is zero,
173      * and 1 if the duration is postive.
174      */

175     public int getSign() {
176         
177         return signum;
178     }
179
180     /**
181      * TODO: Javadoc
182      * @param isPositive Sign.
183      *
184      * @return 1 if positive, else -1.
185      */

186     private int calcSignum(boolean isPositive) {
187         if ((years == null || years.signum() == 0)
188             && (months == null || months.signum() == 0)
189             && (days == null || days.signum() == 0)
190             && (hours == null || hours.signum() == 0)
191             && (minutes == null || minutes.signum() == 0)
192             && (seconds == null || seconds.signum() == 0)) {
193             return 0;
194             }
195
196             if (isPositive) {
197                 return 1;
198             } else {
199                 return -1;
200             }
201
202     }
203     
204     /**
205      * <p>Constructs a new Duration object by specifying each field individually.</p>
206      *
207      * <p>All the parameters are optional as long as at least one field is present.
208      * If specified, parameters have to be zero or positive.</p>
209      *
210      * @param isPositive Set to <code>false</code> to create a negative duration. When the length
211      * of the duration is zero, this parameter will be ignored.
212      * @param years of this <code>Duration</code>
213      * @param months of this <code>Duration</code>
214      * @param days of this <code>Duration</code>
215      * @param hours of this <code>Duration</code>
216      * @param minutes of this <code>Duration</code>
217      * @param seconds of this <code>Duration</code>
218      *
219      * @throws IllegalArgumentException
220      * If years, months, days, hours, minutes and
221      * seconds parameters are all <code>null</code>. Or if any
222      * of those parameters are negative.
223      */

224     protected DurationImpl(
225         boolean isPositive,
226         BigInteger JavaDoc years,
227         BigInteger JavaDoc months,
228         BigInteger JavaDoc days,
229         BigInteger JavaDoc hours,
230         BigInteger JavaDoc minutes,
231         BigDecimal JavaDoc seconds) {
232             
233         this.years = years;
234         this.months = months;
235         this.days = days;
236         this.hours = hours;
237         this.minutes = minutes;
238         this.seconds = seconds;
239
240         this.signum = calcSignum(isPositive);
241
242         // sanity check
243
if (years == null
244             && months == null
245             && days == null
246             && hours == null
247             && minutes == null
248             && seconds == null) {
249             throw new IllegalArgumentException JavaDoc(
250             //"all the fields are null"
251
DatatypeMessageFormatter.formatMessage(null, "AllFieldsNull", null)
252             );
253         }
254         testNonNegative(years, DatatypeConstants.YEARS);
255         testNonNegative(months, DatatypeConstants.MONTHS);
256         testNonNegative(days, DatatypeConstants.DAYS);
257         testNonNegative(hours, DatatypeConstants.HOURS);
258         testNonNegative(minutes, DatatypeConstants.MINUTES);
259         testNonNegative(seconds, DatatypeConstants.SECONDS);
260     }
261     
262     /**
263      * <p>Makes sure that the given number is non-negative. If it is not,
264      * throw {@link IllegalArgumentException}.</p>
265      *
266      * @param n Number to test.
267      * @param f Field to test.
268      */

269     private static void testNonNegative(BigInteger JavaDoc n, DatatypeConstants.Field JavaDoc f) {
270         if (n != null && n.signum() < 0) {
271             throw new IllegalArgumentException JavaDoc(
272                 DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object JavaDoc[]{f.toString()})
273             );
274         }
275     }
276     
277     /**
278      * <p>Makes sure that the given number is non-negative. If it is not,
279      * throw {@link IllegalArgumentException}.</p>
280      *
281      * @param n Number to test.
282      * @param f Field to test.
283      */

284     private static void testNonNegative(BigDecimal JavaDoc n, DatatypeConstants.Field JavaDoc f) {
285         if (n != null && n.signum() < 0) {
286             
287             throw new IllegalArgumentException JavaDoc(
288                 DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object JavaDoc[]{f.toString()})
289             );
290         }
291     }
292     
293     /**
294      * <p>Constructs a new Duration object by specifying each field
295      * individually.</p>
296      *
297      * <p>This method is functionally equivalent to
298      * invoking another constructor by wrapping
299      * all non-zero parameters into {@link BigInteger} and {@link BigDecimal}.
300      * Zero value of int parameter is equivalent of null value of
301      * the corresponding field.</p>
302      *
303      * @see #DurationImpl(boolean, BigInteger, BigInteger, BigInteger, BigInteger,
304      * BigInteger, BigDecimal)
305      */

306     protected DurationImpl(
307         final boolean isPositive,
308         final int years,
309         final int months,
310         final int days,
311         final int hours,
312         final int minutes,
313         final int seconds) {
314         this(
315             isPositive,
316             wrap(years),
317             wrap(months),
318             wrap(days),
319             wrap(hours),
320             wrap(minutes),
321             seconds != 0 ? new BigDecimal JavaDoc(String.valueOf(seconds)) : null);
322     }
323
324     /**
325      * TODO: Javadoc
326      *
327      * @param i int to convert to BigInteger.
328      *
329      * @return BigInteger representation of int.
330      */

331     private static BigInteger JavaDoc wrap(final int i) {
332         
333         // field may not be set
334
if (i == DatatypeConstants.FIELD_UNDEFINED) {
335             return null;
336         }
337         
338         // int -> BigInteger
339
return new BigInteger JavaDoc(String.valueOf(i));
340     }
341     
342     /**
343      * <p>Constructs a new Duration object by specifying the duration
344      * in milliseconds.</p>
345      *
346      * <p>The DAYS, HOURS, MINUTES and SECONDS fields are used to
347      * represent the specifed duration in a reasonable way.
348      * That is, the constructed object <code>x</code> satisfies
349      * the following conditions:</p>
350      * <ul>
351      * <li>x.getHours()&lt;24
352      * <li>x.getMinutes()&lt;60
353      * <li>x.getSeconds()&lt;60
354      * </ul>
355      *
356      * @param durationInMilliSeconds
357      * The length of the duration in milliseconds.
358      */

359     protected DurationImpl(final long durationInMilliSeconds) {
360         
361         boolean is0x8000000000000000L = false;
362         long l = durationInMilliSeconds;
363         
364         if (l > 0) {
365             signum = 1;
366         } else if (l < 0) {
367             signum = -1;
368             if (l == 0x8000000000000000L) {
369                 // negating 0x8000000000000000L causes an overflow
370
l++;
371                 is0x8000000000000000L = true;
372             }
373             l *= -1;
374         } else {
375             signum = 0;
376         }
377         
378         this.years = null;
379         this.months = null;
380         
381         this.seconds =
382             BigDecimal.valueOf((l % 60000L) + (is0x8000000000000000L ? 1 : 0), 3);
383         
384         l /= 60000L;
385         this.minutes = (l == 0) ? null : BigInteger.valueOf(l % 60L);
386
387         l /= 60L;
388         this.hours = (l == 0) ? null : BigInteger.valueOf(l % 24L);
389         
390         l /= 24L;
391         this.days = (l == 0) ? null : BigInteger.valueOf(l);
392     }
393     
394     
395     /**
396      * Constructs a new Duration object by
397      * parsing its string representation
398      * "PnYnMnDTnHnMnS" as defined in XML Schema 1.0 section 3.2.6.1.
399      *
400      * <p>
401      * The string representation may not have any leading
402      * and trailing whitespaces.
403      *
404      * <p>
405      * For example, this method parses strings like
406      * "P1D" (1 day), "-PT100S" (-100 sec.), "P1DT12H" (1 days and 12 hours).
407      *
408      * <p>
409      * The parsing is done field by field so that
410      * the following holds for any lexically correct string x:
411      * <pre>
412      * new Duration(x).toString().equals(x)
413      * </pre>
414      *
415      * Returns a non-null valid duration object that holds the value
416      * indicated by the lexicalRepresentation parameter.
417      *
418      * @param lexicalRepresentation
419      * Lexical representation of a duration.
420      * @throws IllegalArgumentException
421      * If the given string does not conform to the aforementioned
422      * specification.
423      * @throws NullPointerException
424      * If the given string is null.
425      */

426     protected DurationImpl(String JavaDoc lexicalRepresentation)
427         throws IllegalArgumentException JavaDoc {
428         // only if I could use the JDK1.4 regular expression ....
429

430         final String JavaDoc s = lexicalRepresentation;
431         boolean positive;
432         int[] idx = new int[1];
433     int length = s.length();
434     boolean timeRequired = false;
435
436     if (lexicalRepresentation == null) {
437         throw new NullPointerException JavaDoc();
438     }
439         
440     idx[0] = 0;
441     if (length != idx[0] && s.charAt(idx[0]) == '-') {
442         idx[0]++;
443         positive = false;
444     } else {
445         positive = true;
446     }
447         
448     if (length != idx[0] && s.charAt(idx[0]++) != 'P') {
449         throw new IllegalArgumentException JavaDoc(s); //,idx[0]-1);
450
}
451
452         
453         // phase 1: chop the string into chunks
454
// (where a chunk is '<number><a symbol>'
455
//--------------------------------------
456
int dateLen = 0;
457         String JavaDoc[] dateParts = new String JavaDoc[3];
458         int[] datePartsIndex = new int[3];
459         while (length != idx[0]
460             && isDigit(s.charAt(idx[0]))
461             && dateLen < 3) {
462             datePartsIndex[dateLen] = idx[0];
463             dateParts[dateLen++] = parsePiece(s, idx);
464         }
465         
466     if (length != idx[0]) {
467         if (s.charAt(idx[0]++) == 'T') {
468         timeRequired = true;
469         } else {
470         throw new IllegalArgumentException JavaDoc(s); // ,idx[0]-1);
471
}
472     }
473         
474         int timeLen = 0;
475         String JavaDoc[] timeParts = new String JavaDoc[3];
476         int[] timePartsIndex = new int[3];
477         while (length != idx[0]
478             && isDigitOrPeriod(s.charAt(idx[0]))
479             && timeLen < 3) {
480             timePartsIndex[timeLen] = idx[0];
481             timeParts[timeLen++] = parsePiece(s, idx);
482         }
483         
484     if (timeRequired && timeLen == 0) {
485             throw new IllegalArgumentException JavaDoc(s); // ,idx[0]);
486
}
487
488         if (length != idx[0]) {
489             throw new IllegalArgumentException JavaDoc(s); // ,idx[0]);
490
}
491         if (dateLen == 0 && timeLen == 0) {
492             throw new IllegalArgumentException JavaDoc(s); // ,idx[0]);
493
}
494         
495         // phase 2: check the ordering of chunks
496
//--------------------------------------
497
organizeParts(s, dateParts, datePartsIndex, dateLen, "YMD");
498         organizeParts(s, timeParts, timePartsIndex, timeLen, "HMS");
499         
500         // parse into numbers
501
years = parseBigInteger(s, dateParts[0], datePartsIndex[0]);
502         months = parseBigInteger(s, dateParts[1], datePartsIndex[1]);
503         days = parseBigInteger(s, dateParts[2], datePartsIndex[2]);
504         hours = parseBigInteger(s, timeParts[0], timePartsIndex[0]);
505         minutes = parseBigInteger(s, timeParts[1], timePartsIndex[1]);
506         seconds = parseBigDecimal(s, timeParts[2], timePartsIndex[2]);
507         signum = calcSignum(positive);
508     }
509         
510      
511     /**
512      * TODO: Javadoc
513      *
514      * @param ch char to test.
515      *
516      * @return true if ch is a digit, else false.
517      */

518     private static boolean isDigit(char ch) {
519         return '0' <= ch && ch <= '9';
520     }
521     
522     /**
523      * TODO: Javadoc
524      *
525      * @param ch to test.
526      *
527      * @return true if ch is a digit or a period, else false.
528      */

529     private static boolean isDigitOrPeriod(char ch) {
530         return isDigit(ch) || ch == '.';
531     }
532     
533     /**
534      * TODO: Javadoc
535      *
536      * @param whole String to parse.
537      * @param idx TODO: ???
538      *
539      * @return Result of parsing.
540      *
541      * @throws IllegalArgumentException If whole cannot be parsed.
542      */

543     private static String JavaDoc parsePiece(String JavaDoc whole, int[] idx)
544         throws IllegalArgumentException JavaDoc {
545         int start = idx[0];
546         while (idx[0] < whole.length()
547             && isDigitOrPeriod(whole.charAt(idx[0]))) {
548             idx[0]++;
549             }
550         if (idx[0] == whole.length()) {
551             throw new IllegalArgumentException JavaDoc(whole); // ,idx[0]);
552
}
553
554         idx[0]++;
555
556         return whole.substring(start, idx[0]);
557     }
558     
559     /**
560      * TODO: Javadoc.
561      *
562      * @param whole TODO: ???
563      * @param parts TODO: ???
564      * @param partsIndex TODO: ???
565      * @param len TODO: ???
566      * @param tokens TODO: ???
567      *
568      * @throws IllegalArgumentException TODO: ???
569      */

570     private static void organizeParts(
571         String JavaDoc whole,
572         String JavaDoc[] parts,
573         int[] partsIndex,
574         int len,
575         String JavaDoc tokens)
576         throws IllegalArgumentException JavaDoc {
577
578         int idx = tokens.length();
579         for (int i = len - 1; i >= 0; i--) {
580             int nidx =
581                 tokens.lastIndexOf(
582                     parts[i].charAt(parts[i].length() - 1),
583                     idx - 1);
584             if (nidx == -1) {
585                 throw new IllegalArgumentException JavaDoc(whole);
586                 // ,partsIndex[i]+parts[i].length()-1);
587
}
588
589             for (int j = nidx + 1; j < idx; j++) {
590                 parts[j] = null;
591             }
592             idx = nidx;
593             parts[idx] = parts[i];
594             partsIndex[idx] = partsIndex[i];
595         }
596         for (idx--; idx >= 0; idx--) {
597             parts[idx] = null;
598         }
599     }
600     
601     /**
602      * TODO: Javadoc
603      *
604      * @param whole TODO: ???.
605      * @param part TODO: ???.
606      * @param index TODO: ???.
607      *
608      * @return TODO: ???.
609      *
610      * @throws IllegalArgumentException TODO: ???.
611      */

612     private static BigInteger JavaDoc parseBigInteger(
613         String JavaDoc whole,
614         String JavaDoc part,
615         int index)
616         throws IllegalArgumentException JavaDoc {
617         if (part == null) {
618             return null;
619         }
620         part = part.substring(0, part.length() - 1);
621         // try {
622
return new BigInteger JavaDoc(part);
623         // } catch( NumberFormatException e ) {
624
// throw new ParseException( whole, index );
625
// }
626
}
627     
628     /**
629      * TODO: Javadoc.
630      *
631      * @param whole TODO: ???.
632      * @param part TODO: ???.
633      * @param index TODO: ???.
634      *
635      * @return TODO: ???.
636      *
637      * @throws IllegalArgumentException TODO: ???.
638      */

639     private static BigDecimal JavaDoc parseBigDecimal(
640         String JavaDoc whole,
641         String JavaDoc part,
642         int index)
643         throws IllegalArgumentException JavaDoc {
644         if (part == null) {
645             return null;
646         }
647         part = part.substring(0, part.length() - 1);
648         // NumberFormatException is IllegalArgumentException
649
// try {
650
return new BigDecimal JavaDoc(part);
651         // } catch( NumberFormatException e ) {
652
// throw new ParseException( whole, index );
653
// }
654
}
655     
656     /**
657      * <p>Four constants defined for the comparison of durations.</p>
658      */

659     private static final XMLGregorianCalendar JavaDoc[] TEST_POINTS = new XMLGregorianCalendar JavaDoc[] {
660         XMLGregorianCalendarImpl.parse("1696-09-01T00:00:00Z"),
661         XMLGregorianCalendarImpl.parse("1697-02-01T00:00:00Z"),
662         XMLGregorianCalendarImpl.parse("1903-03-01T00:00:00Z"),
663         XMLGregorianCalendarImpl.parse("1903-07-01T00:00:00Z")
664     };
665     
666     /**
667      * <p>Partial order relation comparison with this <code>Duration</code> instance.</p>
668      *
669      * <p>Comparison result must be in accordance with
670      * <a HREF="http://www.w3.org/TR/xmlschema-2/#duration-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2,
671      * <i>Order relation on duration</i></a>.</p>
672      *
673      * <p>Return:</p>
674      * <ul>
675      * <li>{@link DatatypeConstants#LESSER} if this <code>Duration</code> is shorter than <code>duration</code> parameter</li>
676      * <li>{@link DatatypeConstants#EQUAL} if this <code>Duration</code> is equal to <code>duration</code> parameter</li>
677      * <li>{@link DatatypeConstants#GREATER} if this <code>Duration</code> is longer than <code>duration</code> parameter</li>
678      * <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li>
679      * </ul>
680      *
681      * @param duration to compare
682      *
683      * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
684      * {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER}
685      * or {@link DatatypeConstants#INDETERMINATE}.
686      *
687      * @throws UnsupportedOperationException If the underlying implementation
688      * cannot reasonably process the request, e.g. W3C XML Schema allows for
689      * arbitrarily large/small/precise values, the request may be beyond the
690      * implementations capability.
691      * @throws NullPointerException if <code>duration</code> is <code>null</code>.
692      *
693      * @see #isShorterThan(Duration)
694      * @see #isLongerThan(Duration)
695      */

696     public int compare(Duration JavaDoc rhs) {
697         
698         BigInteger JavaDoc maxintAsBigInteger = BigInteger.valueOf((long) Integer.MAX_VALUE);
699         BigInteger JavaDoc minintAsBigInteger = BigInteger.valueOf((long) Integer.MIN_VALUE);
700
701         // check for fields that are too large in this Duration
702
if (years != null && years.compareTo(maxintAsBigInteger) == 1) {
703             throw new UnsupportedOperationException JavaDoc(
704                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
705                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), years.toString()})
706                         //this.getClass().getName() + "#compare(Duration duration)"
707
//+ " years too large to be supported by this implementation "
708
//+ years.toString()
709
);
710         }
711         if (months != null && months.compareTo(maxintAsBigInteger) == 1) {
712             throw new UnsupportedOperationException JavaDoc(
713                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
714                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), months.toString()})
715             
716                         //this.getClass().getName() + "#compare(Duration duration)"
717
//+ " months too large to be supported by this implementation "
718
//+ months.toString()
719
);
720         }
721         if (days != null && days.compareTo(maxintAsBigInteger) == 1) {
722             throw new UnsupportedOperationException JavaDoc(
723                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
724                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.DAYS.toString(), days.toString()})
725             
726                         //this.getClass().getName() + "#compare(Duration duration)"
727
//+ " days too large to be supported by this implementation "
728
//+ days.toString()
729
);
730         }
731         if (hours != null && hours.compareTo(maxintAsBigInteger) == 1) {
732             throw new UnsupportedOperationException JavaDoc(
733                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
734                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), hours.toString()})
735             
736                         //this.getClass().getName() + "#compare(Duration duration)"
737
//+ " hours too large to be supported by this implementation "
738
//+ hours.toString()
739
);
740         }
741         if (minutes != null && minutes.compareTo(maxintAsBigInteger) == 1) {
742             throw new UnsupportedOperationException JavaDoc(
743                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
744                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), minutes.toString()})
745             
746                         //this.getClass().getName() + "#compare(Duration duration)"
747
//+ " minutes too large to be supported by this implementation "
748
//+ minutes.toString()
749
);
750         }
751         if (seconds != null && seconds.toBigInteger().compareTo(maxintAsBigInteger) == 1) {
752             throw new UnsupportedOperationException JavaDoc(
753                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
754                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), seconds.toString()})
755             
756                         //this.getClass().getName() + "#compare(Duration duration)"
757
//+ " seconds too large to be supported by this implementation "
758
//+ seconds.toString()
759
);
760         }
761         
762         // check for fields that are too large in rhs Duration
763
BigInteger JavaDoc rhsYears = (BigInteger JavaDoc) rhs.getField(DatatypeConstants.YEARS);
764         if (rhsYears != null && rhsYears.compareTo(maxintAsBigInteger) == 1) {
765             throw new UnsupportedOperationException JavaDoc(
766                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
767                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), rhsYears.toString()})
768             
769                         //this.getClass().getName() + "#compare(Duration duration)"
770
//+ " years too large to be supported by this implementation "
771
//+ rhsYears.toString()
772
);
773         }
774         BigInteger JavaDoc rhsMonths = (BigInteger JavaDoc) rhs.getField(DatatypeConstants.MONTHS);
775         if (rhsMonths != null && rhsMonths.compareTo(maxintAsBigInteger) == 1) {
776             throw new UnsupportedOperationException JavaDoc(
777                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
778                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), rhsMonths.toString()})
779             
780                         //this.getClass().getName() + "#compare(Duration duration)"
781
//+ " months too large to be supported by this implementation "
782
//+ rhsMonths.toString()
783
);
784         }
785         BigInteger JavaDoc rhsDays = (BigInteger JavaDoc) rhs.getField(DatatypeConstants.DAYS);
786         if (rhsDays != null && rhsDays.compareTo(maxintAsBigInteger) == 1) {
787             throw new UnsupportedOperationException JavaDoc(
788                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
789                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.DAYS.toString(), rhsDays.toString()})
790             
791                         //this.getClass().getName() + "#compare(Duration duration)"
792
//+ " days too large to be supported by this implementation "
793
//+ rhsDays.toString()
794
);
795         }
796         BigInteger JavaDoc rhsHours = (BigInteger JavaDoc) rhs.getField(DatatypeConstants.HOURS);
797         if (rhsHours != null && rhsHours.compareTo(maxintAsBigInteger) == 1) {
798             throw new UnsupportedOperationException JavaDoc(
799                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
800                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), rhsHours.toString()})
801             
802                         //this.getClass().getName() + "#compare(Duration duration)"
803
//+ " hours too large to be supported by this implementation "
804
//+ rhsHours.toString()
805
);
806         }
807         BigInteger JavaDoc rhsMinutes = (BigInteger JavaDoc) rhs.getField(DatatypeConstants.MINUTES);
808         if (rhsMinutes != null && rhsMinutes.compareTo(maxintAsBigInteger) == 1) {
809             throw new UnsupportedOperationException JavaDoc(
810                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
811                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), rhsMinutes.toString()})
812             
813                         //this.getClass().getName() + "#compare(Duration duration)"
814
//+ " minutes too large to be supported by this implementation "
815
//+ rhsMinutes.toString()
816
);
817         }
818         BigDecimal JavaDoc rhsSecondsAsBigDecimal = (BigDecimal JavaDoc) rhs.getField(DatatypeConstants.SECONDS);
819         BigInteger JavaDoc rhsSeconds = null;
820         if ( rhsSecondsAsBigDecimal != null ) {
821                 rhsSeconds = rhsSecondsAsBigDecimal.toBigInteger();
822         }
823         if (rhsSeconds != null && rhsSeconds.compareTo(maxintAsBigInteger) == 1) {
824             throw new UnsupportedOperationException JavaDoc(
825                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
826                             new Object JavaDoc[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), rhsSeconds.toString()})
827             
828                         //this.getClass().getName() + "#compare(Duration duration)"
829
//+ " seconds too large to be supported by this implementation "
830
//+ rhsSeconds.toString()
831
);
832         }
833
834         // turn this Duration into a GregorianCalendar
835
GregorianCalendar JavaDoc lhsCalendar = new GregorianCalendar JavaDoc(
836                 1970,
837                 1,
838                 1,
839                 0,
840                 0,
841                 0);
842         lhsCalendar.add(GregorianCalendar.YEAR, getYears() * getSign());
843         lhsCalendar.add(GregorianCalendar.MONTH, getMonths() * getSign());
844         lhsCalendar.add(GregorianCalendar.DAY_OF_YEAR, getDays() * getSign());
845         lhsCalendar.add(GregorianCalendar.HOUR_OF_DAY, getHours() * getSign());
846         lhsCalendar.add(GregorianCalendar.MINUTE, getMinutes() * getSign());
847         lhsCalendar.add(GregorianCalendar.SECOND, getSeconds() * getSign());
848         
849         // turn compare Duration into a GregorianCalendar
850
GregorianCalendar JavaDoc rhsCalendar = new GregorianCalendar JavaDoc(
851                 1970,
852                 1,
853                 1,
854                 0,
855                 0,
856                 0);
857         rhsCalendar.add(GregorianCalendar.YEAR, rhs.getYears() * rhs.getSign());
858         rhsCalendar.add(GregorianCalendar.MONTH, rhs.getMonths() * rhs.getSign());
859         rhsCalendar.add(GregorianCalendar.DAY_OF_YEAR, rhs.getDays() * rhs.getSign());
860         rhsCalendar.add(GregorianCalendar.HOUR_OF_DAY, rhs.getHours() * rhs.getSign());
861         rhsCalendar.add(GregorianCalendar.MINUTE, rhs.getMinutes() * rhs.getSign());
862         rhsCalendar.add(GregorianCalendar.SECOND, rhs.getSeconds() * rhs.getSign());
863         
864         if (lhsCalendar.before(rhsCalendar)) {
865             return DatatypeConstants.LESSER;
866         }
867         
868         if (lhsCalendar.after(rhsCalendar)) {
869             return DatatypeConstants.GREATER;
870         }
871         
872         if (lhsCalendar.equals(rhsCalendar)) {
873             return DatatypeConstants.EQUAL;
874         }
875
876         return DatatypeConstants.INDETERMINATE;
877     }
878     
879     /**
880      * Returns a hash code consistent with the definition of the equals method.
881      *
882      * @see Object#hashCode()
883      */

884     public int hashCode() {
885         // component wise hash is not correct because 1day = 24hours
886
Calendar JavaDoc cal = TEST_POINTS[0].toGregorianCalendar();
887     this.addTo(cal);
888     return (int) getCalendarTimeInMillis(cal);
889     }
890     
891     /**
892      * Returns a string representation of this duration object.
893      *
894      * <p>
895      * The result is formatter according to the XML Schema 1.0
896      * spec and can be always parsed back later into the
897      * equivalent duration object by
898      * the {@link #DurationImpl(String)} constructor.
899      *
900      * <p>
901      * Formally, the following holds for any {@link Duration}
902      * object x.
903      * <pre>
904      * new Duration(x.toString()).equals(x)
905      * </pre>
906      *
907      * @return
908      * Always return a non-null valid String object.
909      */

910     public String JavaDoc toString() {
911         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
912         if (signum < 0) {
913             buf.append('-');
914         }
915         buf.append('P');
916         
917         if (years != null) {
918             buf.append(years + "Y");
919         }
920         if (months != null) {
921             buf.append(months + "M");
922         }
923         if (days != null) {
924             buf.append(days + "D");
925         }
926
927         if (hours != null || minutes != null || seconds != null) {
928             buf.append('T');
929             if (hours != null) {
930                 buf.append(hours + "H");
931             }
932             if (minutes != null) {
933                 buf.append(minutes + "M");
934             }
935             if (seconds != null) {
936                 buf.append(toString(seconds) + "S");
937             }
938         }
939         
940         return buf.toString();
941     }
942
943     /**
944      * <p>Turns {@link BigDecimal} to a string representation.</p>
945      *
946      * <p>Due to a behavior change in the {@link BigDecimal#toString()}
947      * method in JDK1.5, this had to be implemented here.</p>
948      *
949      * @param bd <code>BigDecimal</code> to format as a <code>String</code>
950      *
951      * @return <code>String</code> representation of <code>BigDecimal</code>
952      */

953     private String JavaDoc toString(BigDecimal JavaDoc bd) {
954         String JavaDoc intString = bd.unscaledValue().toString();
955         int scale = bd.scale();
956
957         if (scale == 0) {
958             return intString;
959         }
960
961         /* Insert decimal point */
962         StringBuffer JavaDoc buf;
963         int insertionPoint = intString.length() - scale;
964         if (insertionPoint == 0) { /* Point goes right before intVal */
965             return "0." + intString;
966         } else if (insertionPoint > 0) { /* Point goes inside intVal */
967             buf = new StringBuffer JavaDoc(intString);
968             buf.insert(insertionPoint, '.');
969         } else { /* We must insert zeros between point and intVal */
970             buf = new StringBuffer JavaDoc(3 - insertionPoint + intString.length());
971             buf.append("0.");
972             for (int i = 0; i < -insertionPoint; i++) {
973                 buf.append('0');
974             }
975             buf.append(intString);
976         }
977         return buf.toString();
978     }
979
980     /**
981      * Checks if a field is set.
982      *
983      * A field of a duration object may or may not be present.
984      * This method can be used to test if a field is present.
985      *
986      * @param field
987      * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
988      * MINUTES, or SECONDS.)
989      * @return
990      * true if the field is present. false if not.
991      *
992      * @throws NullPointerException
993      * If the field parameter is null.
994      */

995     public boolean isSet(DatatypeConstants.Field JavaDoc field) {
996         
997         if (field == null) {
998             String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)" ;
999             throw new NullPointerException JavaDoc(
1000                //"cannot be called with field == null"
1001
DatatypeMessageFormatter.formatMessage(null, "FieldCannotBeNull", new Object JavaDoc[]{methodName})
1002            );
1003        }
1004        
1005        if (field == DatatypeConstants.YEARS) {
1006            return years != null;
1007        }
1008
1009        if (field == DatatypeConstants.MONTHS) {
1010            return months != null;
1011        }
1012
1013        if (field == DatatypeConstants.DAYS) {
1014            return days != null;
1015        }
1016
1017        if (field == DatatypeConstants.HOURS) {
1018            return hours != null;
1019        }
1020
1021        if (field == DatatypeConstants.MINUTES) {
1022            return minutes != null;
1023        }
1024        
1025        if (field == DatatypeConstants.SECONDS) {
1026            return seconds != null;
1027        }
1028        String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)";
1029        
1030        throw new IllegalArgumentException JavaDoc(
1031            DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object JavaDoc[]{methodName, field.toString()})
1032        );
1033        
1034    }
1035    
1036    /**
1037     * Gets the value of a field.
1038     *
1039     * Fields of a duration object may contain arbitrary large value.
1040     * Therefore this method is designed to return a {@link Number} object.
1041     *
1042     * In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned
1043     * number will be a non-negative integer. In case of seconds,
1044     * the returned number may be a non-negative decimal value.
1045     *
1046     * @param field
1047     * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1048     * MINUTES, or SECONDS.)
1049     * @return
1050     * If the specified field is present, this method returns
1051     * a non-null non-negative {@link Number} object that
1052     * represents its value. If it is not present, return null.
1053     * For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method
1054     * returns a {@link BigInteger} object. For SECONDS, this
1055     * method returns a {@link BigDecimal}.
1056     *
1057     * @throws NullPointerException
1058     * If the field parameter is null.
1059     */

1060    public Number JavaDoc getField(DatatypeConstants.Field JavaDoc field) {
1061
1062        if (field == null) {
1063            String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field) " ;
1064            
1065            throw new NullPointerException JavaDoc(
1066                DatatypeMessageFormatter.formatMessage(null,"FieldCannotBeNull", new Object JavaDoc[]{methodName})
1067                );
1068        }
1069        
1070        if (field == DatatypeConstants.YEARS) {
1071            return years;
1072        }
1073
1074        if (field == DatatypeConstants.MONTHS) {
1075            return months;
1076        }
1077
1078        if (field == DatatypeConstants.DAYS) {
1079            return days;
1080        }
1081
1082        if (field == DatatypeConstants.HOURS) {
1083            return hours;
1084        }
1085
1086        if (field == DatatypeConstants.MINUTES) {
1087            return minutes;
1088        }
1089        
1090        if (field == DatatypeConstants.SECONDS) {
1091            return seconds;
1092        }
1093        /**
1094        throw new IllegalArgumentException(
1095            "javax.xml.datatype.Duration"
1096            + "#(getSet(DatatypeConstants.Field field) called with an unknown field: "
1097            + field.toString()
1098        );
1099        */

1100        String JavaDoc methodName = "javax.xml.datatype.Duration" + "#(getSet(DatatypeConstants.Field field)";
1101        
1102        throw new IllegalArgumentException JavaDoc(
1103            DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object JavaDoc[]{methodName, field.toString()})
1104        );
1105        
1106    }
1107    
1108    /**
1109     * Obtains the value of the YEARS field as an integer value,
1110     * or 0 if not present.
1111     *
1112     * <p>
1113     * This method is a convenience method around the
1114     * {@link #getField(DatatypeConstants.Field)} method.
1115     *
1116     * <p>
1117     * Note that since this method returns <tt>int</tt>, this
1118     * method will return an incorrect value for {@link Duration}s
1119     * with the year field that goes beyond the range of <tt>int</tt>.
1120     * Use <code>getField(YEARS)</code> to avoid possible loss of precision.</p>
1121     *
1122     * @return
1123     * If the YEARS field is present, return
1124     * its value as an integer by using the {@link Number#intValue()}
1125     * method. If the YEARS field is not present, return 0.
1126     */

1127    public int getYears() {
1128        return getInt(DatatypeConstants.YEARS);
1129    }
1130    
1131    /**
1132     * Obtains the value of the MONTHS field as an integer value,
1133     * or 0 if not present.
1134     *
1135     * This method works just like {@link #getYears()} except
1136     * that this method works on the MONTHS field.
1137     *
1138     * @return Months of this <code>Duration</code>.
1139     */

1140    public int getMonths() {
1141        return getInt(DatatypeConstants.MONTHS);
1142    }
1143    
1144    /**
1145     * Obtains the value of the DAYS field as an integer value,
1146     * or 0 if not present.
1147     *
1148     * This method works just like {@link #getYears()} except
1149     * that this method works on the DAYS field.
1150     *
1151     * @return Days of this <code>Duration</code>.
1152     */

1153    public int getDays() {
1154        return getInt(DatatypeConstants.DAYS);
1155    }
1156    
1157    /**
1158     * Obtains the value of the HOURS field as an integer value,
1159     * or 0 if not present.
1160     *
1161     * This method works just like {@link #getYears()} except
1162     * that this method works on the HOURS field.
1163     *
1164     * @return Hours of this <code>Duration</code>.
1165     *
1166     */

1167    public int getHours() {
1168        return getInt(DatatypeConstants.HOURS);
1169    }
1170    
1171    /**
1172     * Obtains the value of the MINUTES field as an integer value,
1173     * or 0 if not present.
1174     *
1175     * This method works just like {@link #getYears()} except
1176     * that this method works on the MINUTES field.
1177     *
1178     * @return Minutes of this <code>Duration</code>.
1179     *
1180     */

1181    public int getMinutes() {
1182        return getInt(DatatypeConstants.MINUTES);
1183    }
1184    
1185    /**
1186     * Obtains the value of the SECONDS field as an integer value,
1187     * or 0 if not present.
1188     *
1189     * This method works just like {@link #getYears()} except
1190     * that this method works on the SECONDS field.
1191     *
1192     * @return seconds in the integer value. The fraction of seconds
1193     * will be discarded (for example, if the actual value is 2.5,
1194     * this method returns 2)
1195     */

1196    public int getSeconds() {
1197        return getInt(DatatypeConstants.SECONDS);
1198    }
1199    
1200    /**
1201     * <p>Return the requested field value as an int.</p>
1202     *
1203     * <p>If field is not set, i.e. == null, 0 is returned.</p>
1204     *
1205     * @param field To get value for.
1206     *
1207     * @return int value of field or 0 if field is not set.
1208     */

1209    private int getInt(DatatypeConstants.Field JavaDoc field) {
1210        Number JavaDoc n = getField(field);
1211        if (n == null) {
1212            return 0;
1213        } else {
1214            return n.intValue();
1215        }
1216    }
1217        
1218    /**
1219     * <p>Returns the length of the duration in milli-seconds.</p>
1220     *
1221     * <p>If the seconds field carries more digits than milli-second order,
1222     * those will be simply discarded (or in other words, rounded to zero.)
1223     * For example, for any Calendar value <code>x<code>,</p>
1224     * <pre>
1225     * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1226     * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1227     * </pre>
1228     *
1229     * <p>
1230     * Note that this method uses the {@link #addTo(Calendar)} method,
1231     * which may work incorectly with {@link Duration} objects with
1232     * very large values in its fields. See the {@link #addTo(Calendar)}
1233     * method for details.
1234     *
1235     * @param startInstant
1236     * The length of a month/year varies. The <code>startInstant</code> is
1237     * used to disambiguate this variance. Specifically, this method
1238     * returns the difference between <code>startInstant</code> and
1239     * <code>startInstant+duration</code>
1240     *
1241     * @return milliseconds between <code>startInstant</code> and
1242     * <code>startInstant</code> plus this <code>Duration</code>
1243     *
1244     * @throws NullPointerException if <code>startInstant</code> parameter
1245     * is null.
1246     *
1247     */

1248    public long getTimeInMillis(final Calendar JavaDoc startInstant) {
1249        Calendar JavaDoc cal = (Calendar JavaDoc) startInstant.clone();
1250        addTo(cal);
1251        return getCalendarTimeInMillis(cal)
1252                    - getCalendarTimeInMillis(startInstant);
1253    }
1254    
1255    /**
1256     * <p>Returns the length of the duration in milli-seconds.</p>
1257     *
1258     * <p>If the seconds field carries more digits than milli-second order,
1259     * those will be simply discarded (or in other words, rounded to zero.)
1260     * For example, for any <code>Date</code> value <code>x<code>,</p>
1261     * <pre>
1262     * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1263     * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1264     * </pre>
1265     *
1266     * <p>
1267     * Note that this method uses the {@link #addTo(Date)} method,
1268     * which may work incorectly with {@link Duration} objects with
1269     * very large values in its fields. See the {@link #addTo(Date)}
1270     * method for details.
1271     *
1272     * @param startInstant
1273     * The length of a month/year varies. The <code>startInstant</code> is
1274     * used to disambiguate this variance. Specifically, this method
1275     * returns the difference between <code>startInstant</code> and
1276     * <code>startInstant+duration</code>.
1277     *
1278     * @throws NullPointerException
1279     * If the startInstant parameter is null.
1280     *
1281     * @return milliseconds between <code>startInstant</code> and
1282     * <code>startInstant</code> plus this <code>Duration</code>
1283     *
1284     * @see #getTimeInMillis(Calendar)
1285     */

1286    public long getTimeInMillis(final Date JavaDoc startInstant) {
1287        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1288        cal.setTime(startInstant);
1289        this.addTo(cal);
1290        return getCalendarTimeInMillis(cal) - startInstant.getTime();
1291    }
1292    
1293// /**
1294
// * Returns an equivalent but "normalized" duration value.
1295
// *
1296
// * Intuitively, the normalization moves YEARS into
1297
// * MONTHS (by x12) and moves DAYS, HOURS, and MINUTES fields
1298
// * into SECONDS (by x86400, x3600, and x60 respectively.)
1299
// *
1300
// *
1301
// * Formally, this method satisfies the following conditions:
1302
// * <ul>
1303
// * <li>x.normalize().equals(x)
1304
// * <li>!x.normalize().isSet(Duration.YEARS)
1305
// * <li>!x.normalize().isSet(Duration.DAYS)
1306
// * <li>!x.normalize().isSet(Duration.HOURS)
1307
// * <li>!x.normalize().isSet(Duration.MINUTES)
1308
// * </ul>
1309
// *
1310
// * @return
1311
// * always return a non-null valid value.
1312
// */
1313
// public Duration normalize() {
1314
// return null;
1315
// }
1316

1317    /**
1318     * <p>Converts the years and months fields into the days field
1319     * by using a specific time instant as the reference point.</p>
1320     *
1321     * <p>For example, duration of one month normalizes to 31 days
1322     * given the start time instance "July 8th 2003, 17:40:32".</p>
1323     *
1324     * <p>Formally, the computation is done as follows:</p>
1325     * <ol>
1326     * <li>The given Calendar object is cloned.
1327     * <li>The years, months and days fields will be added to
1328     * the {@link Calendar} object
1329     * by using the {@link Calendar#add(int,int)} method.
1330     * <li>The difference between two Calendars are computed in terms of days.
1331     * <li>The computed days, along with the hours, minutes and seconds
1332     * fields of this duration object is used to construct a new
1333     * Duration object.
1334     * </ol>
1335     *
1336     * <p>Note that since the Calendar class uses <code>int</code> to
1337     * hold the value of year and month, this method may produce
1338     * an unexpected result if this duration object holds
1339     * a very large value in the years or months fields.</p>
1340     *
1341     * @param startTimeInstant <code>Calendar</code> reference point.
1342     *
1343     * @return <code>Duration</code> of years and months of this <code>Duration</code> as days.
1344     *
1345     * @throws NullPointerException If the startTimeInstant parameter is null.
1346     */

1347    public Duration JavaDoc normalizeWith(Calendar JavaDoc startTimeInstant) {
1348        
1349        Calendar JavaDoc c = (Calendar JavaDoc) startTimeInstant.clone();
1350        
1351        // using int may cause overflow, but
1352
// Calendar internally treats value as int anyways.
1353
c.add(Calendar.YEAR, getYears() * signum);
1354        c.add(Calendar.MONTH, getMonths() * signum);
1355        c.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1356        
1357        // obtain the difference in terms of days
1358
long diff = getCalendarTimeInMillis(c) - getCalendarTimeInMillis(startTimeInstant);
1359        int days = (int) (diff / (1000L * 60L * 60L * 24L));
1360        
1361        return new DurationImpl(
1362            days >= 0,
1363            null,
1364            null,
1365            wrap(Math.abs(days)),
1366            (BigInteger JavaDoc) getField(DatatypeConstants.HOURS),
1367            (BigInteger JavaDoc) getField(DatatypeConstants.MINUTES),
1368            (BigDecimal JavaDoc) getField(DatatypeConstants.SECONDS));
1369    }
1370    
1371    /**
1372     * <p>Computes a new duration whose value is <code>factor</code> times
1373     * longer than the value of this duration.</p>
1374     *
1375     * <p>This method is provided for the convenience.
1376     * It is functionally equivalent to the following code:</p>
1377     * <pre>
1378     * multiply(new BigDecimal(String.valueOf(factor)))
1379     * </pre>
1380     *
1381     * @param factor Factor times longer of new <code>Duration</code> to create.
1382     *
1383     * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>.
1384     *
1385     * @see #multiply(BigDecimal)
1386     */

1387    public Duration JavaDoc multiply(int factor) {
1388        return multiply(BigDecimal.valueOf(factor));
1389    }
1390    
1391    /**
1392     * Computes a new duration whose value is <code>factor</code> times
1393     * longer than the value of this duration.
1394     *
1395     * <p>
1396     * For example,
1397     * <pre>
1398     * "P1M" (1 month) * "12" = "P12M" (12 months)
1399     * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
1400     * "P1M" (1 month) * "1.5" = IllegalStateException
1401     * </pre>
1402     *
1403     * <p>
1404     * Since the {@link Duration} class is immutable, this method
1405     * doesn't change the value of this object. It simply computes
1406     * a new Duration object and returns it.
1407     *
1408     * <p>
1409     * The operation will be performed field by field with the precision
1410     * of {@link BigDecimal}. Since all the fields except seconds are
1411     * restricted to hold integers,
1412     * any fraction produced by the computation will be
1413     * carried down toward the next lower unit. For example,
1414     * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,
1415     * which will be carried down to "PT12H" (12 hours).
1416     * When fractions of month cannot be meaningfully carried down
1417     * to days, or year to months, this will cause an
1418     * {@link IllegalStateException} to be thrown.
1419     * For example if you multiple one month by 0.5.</p>
1420     *
1421     * <p>
1422     * To avoid {@link IllegalStateException}, use
1423     * the {@link #normalizeWith(Calendar)} method to remove the years
1424     * and months fields.
1425     *
1426     * @param factor to multiply by
1427     *
1428     * @return
1429     * returns a non-null valid {@link Duration} object
1430     *
1431     * @throws IllegalStateException if operation produces fraction in
1432     * the months field.
1433     *
1434     * @throws NullPointerException if the <code>factor</code> parameter is
1435     * <code>null</code>.
1436     *
1437     */

1438    public Duration JavaDoc multiply(BigDecimal JavaDoc factor) {
1439        BigDecimal JavaDoc carry = ZERO;
1440        int factorSign = factor.signum();
1441        factor = factor.abs();
1442        
1443        BigDecimal JavaDoc[] buf = new BigDecimal JavaDoc[6];
1444        
1445        for (int i = 0; i < 5; i++) {
1446            BigDecimal JavaDoc bd = getFieldAsBigDecimal(FIELDS[i]);
1447            bd = bd.multiply(factor).add(carry);
1448            
1449            buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);
1450            
1451            bd = bd.subtract(buf[i]);
1452            if (i == 1) {
1453                if (bd.signum() != 0) {
1454                    throw new IllegalStateException JavaDoc(); // illegal carry-down
1455
} else {
1456                    carry = ZERO;
1457                }
1458            } else {
1459                carry = bd.multiply(FACTORS[i]);
1460            }
1461        }
1462        
1463        if (seconds != null) {
1464            buf[5] = seconds.multiply(factor).add(carry);
1465        } else {
1466            buf[5] = carry;
1467        }
1468                
1469        return new DurationImpl(
1470            this.signum * factorSign >= 0,
1471            toBigInteger(buf[0], null == years),
1472            toBigInteger(buf[1], null == months),
1473            toBigInteger(buf[2], null == days),
1474            toBigInteger(buf[3], null == hours),
1475            toBigInteger(buf[4], null == minutes),
1476            (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
1477    }
1478    
1479    /**
1480     * <p>Gets the value of the field as a {@link BigDecimal}.</p>
1481     *
1482     * <p>If the field is unset, return 0.</p>
1483     *
1484     * @param f Field to get value for.
1485     *
1486     * @return non-null valid {@link BigDecimal}.
1487     */

1488    private BigDecimal JavaDoc getFieldAsBigDecimal(DatatypeConstants.Field JavaDoc f) {
1489        if (f == DatatypeConstants.SECONDS) {
1490            if (seconds != null) {
1491                return seconds;
1492            } else {
1493                return ZERO;
1494            }
1495        } else {
1496            BigInteger JavaDoc bi = (BigInteger JavaDoc) getField(f);
1497            if (bi == null) {
1498                return ZERO;
1499            } else {
1500                return new BigDecimal JavaDoc(bi);
1501            }
1502        }
1503    }
1504    
1505    /**
1506     * <p>BigInteger value of BigDecimal value.</p>
1507     *
1508     * @param value Value to convert.
1509     * @param canBeNull Can returned value be null?
1510     *
1511     * @return BigInteger value of BigDecimal, possibly null.
1512     */

1513    private static BigInteger JavaDoc toBigInteger(
1514        BigDecimal JavaDoc value,
1515        boolean canBeNull) {
1516        if (canBeNull && value.signum() == 0) {
1517            return null;
1518        } else {
1519            return value.unscaledValue();
1520        }
1521    }
1522    
1523    /**
1524     * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
1525     * FIELDS[i+1].
1526     */

1527    private static final BigDecimal JavaDoc[] FACTORS = new BigDecimal JavaDoc[]{
1528        BigDecimal.valueOf(12),
1529        null/*undefined*/,
1530        BigDecimal.valueOf(24),
1531        BigDecimal.valueOf(60),
1532        BigDecimal.valueOf(60)
1533    };
1534    
1535    /**
1536     * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
1537     *
1538     * <p>For example,</p>
1539     * <pre>
1540     * "1 day" + "-3 days" = "-2 days"
1541     * "1 year" + "1 day" = "1 year and 1 day"
1542     * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
1543     * "15 hours" + "-3 days" = "-(2 days,9 hours)"
1544     * "1 year" + "-1 day" = IllegalStateException
1545     * </pre>
1546     *
1547     * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1548     * there are cases where the operation fails in
1549     * {@link IllegalStateException}.</p>
1550     *
1551     * <p>
1552     * Formally, the computation is defined as follows.</p>
1553     * <p>
1554     * Firstly, we can assume that two {@link Duration}s to be added
1555     * are both positive without losing generality (i.e.,
1556     * <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>,
1557     * <code>(-X)+(-Y)=-(X+Y)</code>)
1558     *
1559     * <p>
1560     * Addition of two positive {@link Duration}s are simply defined as
1561     * field by field addition where missing fields are treated as 0.
1562     * <p>
1563     * A field of the resulting {@link Duration} will be unset if and
1564     * only if respective fields of two input {@link Duration}s are unset.
1565     * <p>
1566     * Note that <code>lhs.add(rhs)</code> will be always successful if
1567     * <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are
1568     * normalized.</p>
1569     *
1570     * @param rhs <code>Duration</code> to add to this <code>Duration</code>
1571     *
1572     * @return
1573     * non-null valid Duration object.
1574     *
1575     * @throws NullPointerException
1576     * If the rhs parameter is null.
1577     * @throws IllegalStateException
1578     * If two durations cannot be meaningfully added. For
1579     * example, adding negative one day to one month causes
1580     * this exception.
1581     *
1582     *
1583     * @see #subtract(Duration)
1584     */

1585    public Duration JavaDoc add(final Duration JavaDoc rhs) {
1586        Duration JavaDoc lhs = this;
1587        BigDecimal JavaDoc[] buf = new BigDecimal JavaDoc[6];
1588        
1589        buf[0] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.YEARS),
1590            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.YEARS), rhs.getSign()));
1591        buf[1] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.MONTHS),
1592            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.MONTHS), rhs.getSign()));
1593        buf[2] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.DAYS),
1594            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.DAYS), rhs.getSign()));
1595        buf[3] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.HOURS),
1596            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.HOURS), rhs.getSign()));
1597        buf[4] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.MINUTES),
1598            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.MINUTES), rhs.getSign()));
1599        buf[5] = sanitize((BigDecimal JavaDoc) lhs.getField(DatatypeConstants.SECONDS),
1600            lhs.getSign()).add(sanitize((BigDecimal JavaDoc) rhs.getField(DatatypeConstants.SECONDS), rhs.getSign()));
1601        
1602        // align sign
1603
alignSigns(buf, 0, 2); // Y,M
1604
alignSigns(buf, 2, 6); // D,h,m,s
1605

1606        // make sure that the sign bit is consistent across all 6 fields.
1607
int s = 0;
1608        for (int i = 0; i < 6; i++) {
1609            if (s * buf[i].signum() < 0) {
1610                throw new IllegalStateException JavaDoc();
1611            }
1612            if (s == 0) {
1613                s = buf[i].signum();
1614            }
1615        }
1616        
1617        return new DurationImpl(
1618            s >= 0,
1619            toBigInteger(sanitize(buf[0], s),
1620                lhs.getField(DatatypeConstants.YEARS) == null && rhs.getField(DatatypeConstants.YEARS) == null),
1621            toBigInteger(sanitize(buf[1], s),
1622                lhs.getField(DatatypeConstants.MONTHS) == null && rhs.getField(DatatypeConstants.MONTHS) == null),
1623            toBigInteger(sanitize(buf[2], s),
1624                lhs.getField(DatatypeConstants.DAYS) == null && rhs.getField(DatatypeConstants.DAYS) == null),
1625            toBigInteger(sanitize(buf[3], s),
1626                lhs.getField(DatatypeConstants.HOURS) == null && rhs.getField(DatatypeConstants.HOURS) == null),
1627            toBigInteger(sanitize(buf[4], s),
1628                lhs.getField(DatatypeConstants.MINUTES) == null && rhs.getField(DatatypeConstants.MINUTES) == null),
1629             (buf[5].signum() == 0
1630             && lhs.getField(DatatypeConstants.SECONDS) == null
1631             && rhs.getField(DatatypeConstants.SECONDS) == null) ? null : sanitize(buf[5], s));
1632    }
1633    
1634    private static void alignSigns(BigDecimal JavaDoc[] buf, int start, int end) {
1635        // align sign
1636
boolean touched;
1637        
1638        do { // repeat until all the sign bits become consistent
1639
touched = false;
1640            int s = 0; // sign of the left fields
1641

1642            for (int i = start; i < end; i++) {
1643                if (s * buf[i].signum() < 0) {
1644                    // this field has different sign than its left field.
1645
touched = true;
1646
1647                    // compute the number of unit that needs to be borrowed.
1648
BigDecimal JavaDoc borrow =
1649                        buf[i].abs().divide(
1650                            FACTORS[i - 1],
1651                            BigDecimal.ROUND_UP);
1652                    if (buf[i].signum() > 0) {
1653                        borrow = borrow.negate();
1654                    }
1655
1656                    // update values
1657
buf[i - 1] = buf[i - 1].subtract(borrow);
1658                    buf[i] = buf[i].add(borrow.multiply(FACTORS[i - 1]));
1659                }
1660                if (buf[i].signum() != 0) {
1661                    s = buf[i].signum();
1662                }
1663            }
1664        } while (touched);
1665    }
1666    
1667    /**
1668     * Compute <code>value*signum</code> where value==null is treated as
1669     * value==0.
1670     * @param value Value to sanitize.
1671     * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1672     *
1673     * @return non-null {@link BigDecimal}.
1674     */

1675    private static BigDecimal JavaDoc sanitize(BigInteger JavaDoc value, int signum) {
1676        if (signum == 0 || value == null) {
1677            return ZERO;
1678        }
1679        if (signum > 0) {
1680            return new BigDecimal JavaDoc(value);
1681        }
1682        return new BigDecimal JavaDoc(value.negate());
1683    }
1684        
1685    /**
1686     * <p>Compute <code>value*signum</code> where <code>value==null</code> is treated as <code>value==0</code></p>.
1687     *
1688     * @param value Value to sanitize.
1689     * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1690     *
1691     * @return non-null {@link BigDecimal}.
1692     */

1693    static BigDecimal JavaDoc sanitize(BigDecimal JavaDoc value, int signum) {
1694        if (signum == 0 || value == null) {
1695            return ZERO;
1696        }
1697        if (signum > 0) {
1698            return value;
1699        }
1700        return value.negate();
1701    }
1702    
1703    /**
1704     * <p>Computes a new duration whose value is <code>this-rhs</code>.</p>
1705     *
1706     * <p>For example:</p>
1707     * <pre>
1708     * "1 day" - "-3 days" = "4 days"
1709     * "1 year" - "1 day" = IllegalStateException
1710     * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)"
1711     * "15 hours" - "-3 days" = "3 days and 15 hours"
1712     * "1 year" - "-1 day" = "1 year and 1 day"
1713     * </pre>
1714     *
1715     * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1716     * there are cases where the operation fails in {@link IllegalStateException}.</p>
1717     *
1718     * <p>Formally the computation is defined as follows.
1719     * First, we can assume that two {@link Duration}s are both positive
1720     * without losing generality. (i.e.,
1721     * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>,
1722     * <code>(-X)-(-Y)=-(X-Y)</code>)</p>
1723     *
1724     * <p>Then two durations are subtracted field by field.
1725     * If the sign of any non-zero field <tt>F</tt> is different from
1726     * the sign of the most significant field,
1727     * 1 (if <tt>F</tt> is negative) or -1 (otherwise)
1728     * will be borrowed from the next bigger unit of <tt>F</tt>.</p>
1729     *
1730     * <p>This process is repeated until all the non-zero fields have
1731     * the same sign.</p>
1732     *
1733     * <p>If a borrow occurs in the days field (in other words, if
1734     * the computation needs to borrow 1 or -1 month to compensate
1735     * days), then the computation fails by throwing an
1736     * {@link IllegalStateException}.</p>
1737     *
1738     * @param rhs <code>Duration</code> to substract from this <code>Duration</code>.
1739     *
1740     * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>.
1741     *
1742     * @throws IllegalStateException
1743     * If two durations cannot be meaningfully subtracted. For
1744     * example, subtracting one day from one month causes
1745     * this exception.
1746     *
1747     * @throws NullPointerException
1748     * If the rhs parameter is null.
1749     *
1750     * @see #add(Duration)
1751     */

1752    public Duration JavaDoc subtract(final Duration JavaDoc rhs) {
1753        return add(rhs.negate());
1754    }
1755    
1756    /**
1757     * Returns a new {@link Duration} object whose
1758     * value is <code>-this</code>.
1759     *
1760     * <p>
1761     * Since the {@link Duration} class is immutable, this method
1762     * doesn't change the value of this object. It simply computes
1763     * a new Duration object and returns it.
1764     *
1765     * @return
1766     * always return a non-null valid {@link Duration} object.
1767     */

1768    public Duration JavaDoc negate() {
1769        return new DurationImpl(
1770            signum <= 0,
1771            years,
1772            months,
1773            days,
1774            hours,
1775            minutes,
1776            seconds);
1777    }
1778    
1779    /**
1780     * Returns the sign of this duration in -1,0, or 1.
1781     *
1782     * @return
1783     * -1 if this duration is negative, 0 if the duration is zero,
1784     * and 1 if the duration is postive.
1785     */

1786    public int signum() {
1787        return signum;
1788    }
1789    
1790    
1791    /**
1792     * Adds this duration to a {@link Calendar} object.
1793     *
1794     * <p>
1795     * Calls {@link java.util.Calendar#add(int,int)} in the
1796     * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS
1797     * if those fields are present. Because the {@link Calendar} class
1798     * uses int to hold values, there are cases where this method
1799     * won't work correctly (for example if values of fields
1800     * exceed the range of int.)
1801     * </p>
1802     *
1803     * <p>
1804     * Also, since this duration class is a Gregorian duration, this
1805     * method will not work correctly if the given {@link Calendar}
1806     * object is based on some other calendar systems.
1807     * </p>
1808     *
1809     * <p>
1810     * Any fractional parts of this {@link Duration} object
1811     * beyond milliseconds will be simply ignored. For example, if
1812     * this duration is "P1.23456S", then 1 is added to SECONDS,
1813     * 234 is added to MILLISECONDS, and the rest will be unused.
1814     * </p>
1815     *
1816     * <p>
1817     * Note that because {@link Calendar#add(int, int)} is using
1818     * <tt>int</tt>, {@link Duration} with values beyond the
1819     * range of <tt>int</tt> in its fields
1820     * will cause overflow/underflow to the given {@link Calendar}.
1821     * {@link XMLGregorianCalendar#add(Duration)} provides the same
1822     * basic operation as this method while avoiding
1823     * the overflow/underflow issues.
1824     *
1825     * @param calendar
1826     * A calendar object whose value will be modified.
1827     * @throws NullPointerException
1828     * if the calendar parameter is null.
1829     */

1830    public void addTo(Calendar JavaDoc calendar) {
1831        calendar.add(Calendar.YEAR, getYears() * signum);
1832        calendar.add(Calendar.MONTH, getMonths() * signum);
1833        calendar.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1834        calendar.add(Calendar.HOUR, getHours() * signum);
1835        calendar.add(Calendar.MINUTE, getMinutes() * signum);
1836        calendar.add(Calendar.SECOND, getSeconds() * signum);
1837
1838        if (seconds != null) {
1839            BigDecimal JavaDoc fraction =
1840                seconds.subtract(seconds.setScale(0, BigDecimal.ROUND_DOWN));
1841            int millisec = fraction.movePointRight(3).intValue();
1842            calendar.add(Calendar.MILLISECOND, millisec * signum);
1843        }
1844    }
1845    
1846    /**
1847     * Adds this duration to a {@link Date} object.
1848     *
1849     * <p>
1850     * The given date is first converted into
1851     * a {@link java.util.GregorianCalendar}, then the duration
1852     * is added exactly like the {@link #addTo(Calendar)} method.
1853     *
1854     * <p>
1855     * The updated time instant is then converted back into a
1856     * {@link Date} object and used to update the given {@link Date} object.
1857     *
1858     * <p>
1859     * This somewhat redundant computation is necessary to unambiguously
1860     * determine the duration of months and years.
1861     *
1862     * @param date
1863     * A date object whose value will be modified.
1864     * @throws NullPointerException
1865     * if the date parameter is null.
1866     */

1867    public void addTo(Date JavaDoc date) {
1868        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1869        cal.setTime(date); // this will throw NPE if date==null
1870
this.addTo(cal);
1871        date.setTime(getCalendarTimeInMillis(cal));
1872    }
1873    
1874    /**
1875     * <p>Stream Unique Identifier.</p>
1876     *
1877     * <p>TODO: Serialization should use the XML string representation as
1878     * the serialization format to ensure future compatibility.</p>
1879     */

1880    private static final long serialVersionUID = 1L;
1881    
1882    /**
1883     * Writes {@link Duration} as a lexical representation
1884     * for maximum future compatibility.
1885     *
1886     * @return
1887     * An object that encapsulates the string
1888     * returned by <code>this.toString()</code>.
1889     */

1890    private Object JavaDoc writeReplace() throws IOException JavaDoc {
1891        return new DurationStream(this.toString());
1892    }
1893    
1894    /**
1895     * Representation of {@link Duration} in the object stream.
1896     *
1897     * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
1898     */

1899    private static class DurationStream implements Serializable JavaDoc {
1900        private final String JavaDoc lexical;
1901
1902        private DurationStream(String JavaDoc _lexical) {
1903            this.lexical = _lexical;
1904        }
1905        
1906        private Object JavaDoc readResolve() throws ObjectStreamException JavaDoc {
1907            // try {
1908
return new DurationImpl(lexical);
1909            // } catch( ParseException e ) {
1910
// throw new StreamCorruptedException("unable to parse "+lexical+" as duration");
1911
// }
1912
}
1913        
1914        private static final long serialVersionUID = 1L;
1915    }
1916    
1917    /**
1918     * Calls the {@link Calendar#getTimeInMillis} method.
1919     * Prior to JDK1.4, this method was protected and therefore
1920     * cannot be invoked directly.
1921     *
1922     * In future, this should be replaced by
1923     * <code>cal.getTimeInMillis()</code>
1924     */

1925    private static long getCalendarTimeInMillis(Calendar JavaDoc cal) {
1926        return cal.getTime().getTime();
1927    }
1928}
1929
1930
Popular Tags