KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > jaxp > datatype > DurationImpl


1 /*
2  * Copyright 2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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

99 class DurationImpl
100     extends Duration JavaDoc
101     implements Serializable JavaDoc {
102     
103     /**
104      * <p>Number of Fields.</p>
105      */

106     private static final int FIELD_NUM = 6;
107     
108     /**
109      * <p>Internal array of value Fields.</p>
110      */

111     private static final DatatypeConstants.Field JavaDoc[] FIELDS = new DatatypeConstants.Field JavaDoc[]{
112             DatatypeConstants.YEARS,
113             DatatypeConstants.MONTHS,
114             DatatypeConstants.DAYS,
115             DatatypeConstants.HOURS,
116             DatatypeConstants.MINUTES,
117             DatatypeConstants.SECONDS
118         };
119
120         /**
121          * <p>Internal array of value Field ids.</p>
122          */

123         private static final int[] FIELD_IDS = {
124                 DatatypeConstants.YEARS.getId(),
125                 DatatypeConstants.MONTHS.getId(),
126                 DatatypeConstants.DAYS.getId(),
127                 DatatypeConstants.HOURS.getId(),
128                 DatatypeConstants.MINUTES.getId(),
129                 DatatypeConstants.SECONDS.getId()
130             };
131
132     /**
133      * <p>BigDecimal value of 0.</p>
134      */

135     private static final BigDecimal JavaDoc ZERO = BigDecimal.valueOf((long) 0);
136
137     /**
138      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
139      * zero, or positive.</p>
140      */

141     private final int signum;
142     
143     /**
144      * <p>Years of this <code>Duration</code>.</p>
145      */

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

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

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

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

166     private final BigInteger JavaDoc minutes;
167     
168     /**
169      * <p>Seconds of this <code>Duration</code>.</p>
170      */

171     private final BigDecimal JavaDoc seconds;
172
173     /**
174      * Returns the sign of this duration in -1,0, or 1.
175      *
176      * @return
177      * -1 if this duration is negative, 0 if the duration is zero,
178      * and 1 if the duration is postive.
179      */

180     public int getSign() {
181         
182         return signum;
183     }
184
185     /**
186      * TODO: Javadoc
187      * @param isPositive Sign.
188      *
189      * @return 1 if positive, else -1.
190      */

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

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

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

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

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

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

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

431     protected DurationImpl(String JavaDoc lexicalRepresentation)
432         throws IllegalArgumentException JavaDoc {
433         // only if I could use the JDK1.4 regular expression ....
434

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

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

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

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

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

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

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

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

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

889     private int compareDates(Duration JavaDoc duration1, Duration JavaDoc duration2) {
890         
891         int resultA = DatatypeConstants.INDETERMINATE;
892         int resultB = DatatypeConstants.INDETERMINATE;
893         
894         XMLGregorianCalendar JavaDoc tempA = (XMLGregorianCalendar JavaDoc)TEST_POINTS[0].clone();
895         XMLGregorianCalendar JavaDoc tempB = (XMLGregorianCalendar JavaDoc)TEST_POINTS[0].clone();
896         
897         //long comparison algorithm is required
898
tempA.add(duration1);
899         tempB.add(duration2);
900         resultA = tempA.compare(tempB);
901         if ( resultA == DatatypeConstants.INDETERMINATE ) {
902             return DatatypeConstants.INDETERMINATE;
903         }
904
905         tempA = (XMLGregorianCalendar JavaDoc)TEST_POINTS[1].clone();
906         tempB = (XMLGregorianCalendar JavaDoc)TEST_POINTS[1].clone();
907         
908         tempA.add(duration1);
909         tempB.add(duration2);
910         resultB = tempA.compare(tempB);
911         resultA = compareResults(resultA, resultB);
912         if (resultA == DatatypeConstants.INDETERMINATE) {
913             return DatatypeConstants.INDETERMINATE;
914         }
915
916         tempA = (XMLGregorianCalendar JavaDoc)TEST_POINTS[2].clone();
917         tempB = (XMLGregorianCalendar JavaDoc)TEST_POINTS[2].clone();
918         
919         tempA.add(duration1);
920         tempB.add(duration2);
921         resultB = tempA.compare(tempB);
922         resultA = compareResults(resultA, resultB);
923         if (resultA == DatatypeConstants.INDETERMINATE) {
924             return DatatypeConstants.INDETERMINATE;
925         }
926
927         tempA = (XMLGregorianCalendar JavaDoc)TEST_POINTS[3].clone();
928         tempB = (XMLGregorianCalendar JavaDoc)TEST_POINTS[3].clone();
929         
930         tempA.add(duration1);
931         tempB.add(duration2);
932         resultB = tempA.compare(tempB);
933         resultA = compareResults(resultA, resultB);
934
935         return resultA;
936     }
937
938     private int compareResults(int resultA, int resultB){
939
940       if ( resultB == DatatypeConstants.INDETERMINATE ) {
941             return DatatypeConstants.INDETERMINATE;
942         }
943         else if ( resultA!=resultB) {
944             return DatatypeConstants.INDETERMINATE;
945         }
946         return resultA;
947     }
948     
949     /**
950      * Returns a hash code consistent with the definition of the equals method.
951      *
952      * @see Object#hashCode()
953      */

954     public int hashCode() {
955         // component wise hash is not correct because 1day = 24hours
956
Calendar JavaDoc cal = TEST_POINTS[0].toGregorianCalendar();
957     this.addTo(cal);
958     return (int) getCalendarTimeInMillis(cal);
959     }
960     
961     /**
962      * Returns a string representation of this duration object.
963      *
964      * <p>
965      * The result is formatter according to the XML Schema 1.0
966      * spec and can be always parsed back later into the
967      * equivalent duration object by
968      * the {@link #DurationImpl(String)} constructor.
969      *
970      * <p>
971      * Formally, the following holds for any {@link Duration}
972      * object x.
973      * <pre>
974      * new Duration(x.toString()).equals(x)
975      * </pre>
976      *
977      * @return
978      * Always return a non-null valid String object.
979      */

980     public String JavaDoc toString() {
981         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
982         if (signum < 0) {
983             buf.append('-');
984         }
985         buf.append('P');
986         
987         if (years != null) {
988             buf.append(years + "Y");
989         }
990         if (months != null) {
991             buf.append(months + "M");
992         }
993         if (days != null) {
994             buf.append(days + "D");
995         }
996
997         if (hours != null || minutes != null || seconds != null) {
998             buf.append('T');
999             if (hours != null) {
1000                buf.append(hours + "H");
1001            }
1002            if (minutes != null) {
1003                buf.append(minutes + "M");
1004            }
1005            if (seconds != null) {
1006                buf.append(toString(seconds) + "S");
1007            }
1008        }
1009        
1010        return buf.toString();
1011    }
1012
1013    /**
1014     * <p>Turns {@link BigDecimal} to a string representation.</p>
1015     *
1016     * <p>Due to a behavior change in the {@link BigDecimal#toString()}
1017     * method in JDK1.5, this had to be implemented here.</p>
1018     *
1019     * @param bd <code>BigDecimal</code> to format as a <code>String</code>
1020     *
1021     * @return <code>String</code> representation of <code>BigDecimal</code>
1022     */

1023    private String JavaDoc toString(BigDecimal JavaDoc bd) {
1024        String JavaDoc intString = bd.unscaledValue().toString();
1025        int scale = bd.scale();
1026
1027        if (scale == 0) {
1028            return intString;
1029        }
1030
1031        /* Insert decimal point */
1032        StringBuffer JavaDoc buf;
1033        int insertionPoint = intString.length() - scale;
1034        if (insertionPoint == 0) { /* Point goes right before intVal */
1035            return "0." + intString;
1036        } else if (insertionPoint > 0) { /* Point goes inside intVal */
1037            buf = new StringBuffer JavaDoc(intString);
1038            buf.insert(insertionPoint, '.');
1039        } else { /* We must insert zeros between point and intVal */
1040            buf = new StringBuffer JavaDoc(3 - insertionPoint + intString.length());
1041            buf.append("0.");
1042            for (int i = 0; i < -insertionPoint; i++) {
1043                buf.append('0');
1044            }
1045            buf.append(intString);
1046        }
1047        return buf.toString();
1048    }
1049
1050    /**
1051     * Checks if a field is set.
1052     *
1053     * A field of a duration object may or may not be present.
1054     * This method can be used to test if a field is present.
1055     *
1056     * @param field
1057     * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1058     * MINUTES, or SECONDS.)
1059     * @return
1060     * true if the field is present. false if not.
1061     *
1062     * @throws NullPointerException
1063     * If the field parameter is null.
1064     */

1065    public boolean isSet(DatatypeConstants.Field JavaDoc field) {
1066        
1067        if (field == null) {
1068            String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)" ;
1069            throw new NullPointerException JavaDoc(
1070                //"cannot be called with field == null"
1071
DatatypeMessageFormatter.formatMessage(null, "FieldCannotBeNull", new Object JavaDoc[]{methodName})
1072            );
1073        }
1074        
1075        if (field == DatatypeConstants.YEARS) {
1076            return years != null;
1077        }
1078
1079        if (field == DatatypeConstants.MONTHS) {
1080            return months != null;
1081        }
1082
1083        if (field == DatatypeConstants.DAYS) {
1084            return days != null;
1085        }
1086
1087        if (field == DatatypeConstants.HOURS) {
1088            return hours != null;
1089        }
1090
1091        if (field == DatatypeConstants.MINUTES) {
1092            return minutes != null;
1093        }
1094        
1095        if (field == DatatypeConstants.SECONDS) {
1096            return seconds != null;
1097        }
1098        String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)";
1099        
1100        throw new IllegalArgumentException JavaDoc(
1101            DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object JavaDoc[]{methodName, field.toString()})
1102        );
1103        
1104    }
1105    
1106    /**
1107     * Gets the value of a field.
1108     *
1109     * Fields of a duration object may contain arbitrary large value.
1110     * Therefore this method is designed to return a {@link Number} object.
1111     *
1112     * In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned
1113     * number will be a non-negative integer. In case of seconds,
1114     * the returned number may be a non-negative decimal value.
1115     *
1116     * @param field
1117     * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1118     * MINUTES, or SECONDS.)
1119     * @return
1120     * If the specified field is present, this method returns
1121     * a non-null non-negative {@link Number} object that
1122     * represents its value. If it is not present, return null.
1123     * For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method
1124     * returns a {@link BigInteger} object. For SECONDS, this
1125     * method returns a {@link BigDecimal}.
1126     *
1127     * @throws NullPointerException
1128     * If the field parameter is null.
1129     */

1130    public Number JavaDoc getField(DatatypeConstants.Field JavaDoc field) {
1131
1132        if (field == null) {
1133            String JavaDoc methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field) " ;
1134            
1135            throw new NullPointerException JavaDoc(
1136                DatatypeMessageFormatter.formatMessage(null,"FieldCannotBeNull", new Object JavaDoc[]{methodName})
1137                );
1138        }
1139        
1140        if (field == DatatypeConstants.YEARS) {
1141            return years;
1142        }
1143
1144        if (field == DatatypeConstants.MONTHS) {
1145            return months;
1146        }
1147
1148        if (field == DatatypeConstants.DAYS) {
1149            return days;
1150        }
1151
1152        if (field == DatatypeConstants.HOURS) {
1153            return hours;
1154        }
1155
1156        if (field == DatatypeConstants.MINUTES) {
1157            return minutes;
1158        }
1159        
1160        if (field == DatatypeConstants.SECONDS) {
1161            return seconds;
1162        }
1163        /**
1164        throw new IllegalArgumentException(
1165            "javax.xml.datatype.Duration"
1166            + "#(getSet(DatatypeConstants.Field field) called with an unknown field: "
1167            + field.toString()
1168        );
1169        */

1170        String JavaDoc methodName = "javax.xml.datatype.Duration" + "#(getSet(DatatypeConstants.Field field)";
1171        
1172        throw new IllegalArgumentException JavaDoc(
1173            DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object JavaDoc[]{methodName, field.toString()})
1174        );
1175        
1176    }
1177    
1178    /**
1179     * Obtains the value of the YEARS field as an integer value,
1180     * or 0 if not present.
1181     *
1182     * <p>
1183     * This method is a convenience method around the
1184     * {@link #getField(DatatypeConstants.Field)} method.
1185     *
1186     * <p>
1187     * Note that since this method returns <tt>int</tt>, this
1188     * method will return an incorrect value for {@link Duration}s
1189     * with the year field that goes beyond the range of <tt>int</tt>.
1190     * Use <code>getField(YEARS)</code> to avoid possible loss of precision.</p>
1191     *
1192     * @return
1193     * If the YEARS field is present, return
1194     * its value as an integer by using the {@link Number#intValue()}
1195     * method. If the YEARS field is not present, return 0.
1196     */

1197    public int getYears() {
1198        return getInt(DatatypeConstants.YEARS);
1199    }
1200    
1201    /**
1202     * Obtains the value of the MONTHS field as an integer value,
1203     * or 0 if not present.
1204     *
1205     * This method works just like {@link #getYears()} except
1206     * that this method works on the MONTHS field.
1207     *
1208     * @return Months of this <code>Duration</code>.
1209     */

1210    public int getMonths() {
1211        return getInt(DatatypeConstants.MONTHS);
1212    }
1213    
1214    /**
1215     * Obtains the value of the DAYS field as an integer value,
1216     * or 0 if not present.
1217     *
1218     * This method works just like {@link #getYears()} except
1219     * that this method works on the DAYS field.
1220     *
1221     * @return Days of this <code>Duration</code>.
1222     */

1223    public int getDays() {
1224        return getInt(DatatypeConstants.DAYS);
1225    }
1226    
1227    /**
1228     * Obtains the value of the HOURS field as an integer value,
1229     * or 0 if not present.
1230     *
1231     * This method works just like {@link #getYears()} except
1232     * that this method works on the HOURS field.
1233     *
1234     * @return Hours of this <code>Duration</code>.
1235     *
1236     */

1237    public int getHours() {
1238        return getInt(DatatypeConstants.HOURS);
1239    }
1240    
1241    /**
1242     * Obtains the value of the MINUTES field as an integer value,
1243     * or 0 if not present.
1244     *
1245     * This method works just like {@link #getYears()} except
1246     * that this method works on the MINUTES field.
1247     *
1248     * @return Minutes of this <code>Duration</code>.
1249     *
1250     */

1251    public int getMinutes() {
1252        return getInt(DatatypeConstants.MINUTES);
1253    }
1254    
1255    /**
1256     * Obtains the value of the SECONDS field as an integer value,
1257     * or 0 if not present.
1258     *
1259     * This method works just like {@link #getYears()} except
1260     * that this method works on the SECONDS field.
1261     *
1262     * @return seconds in the integer value. The fraction of seconds
1263     * will be discarded (for example, if the actual value is 2.5,
1264     * this method returns 2)
1265     */

1266    public int getSeconds() {
1267        return getInt(DatatypeConstants.SECONDS);
1268    }
1269    
1270    /**
1271     * <p>Return the requested field value as an int.</p>
1272     *
1273     * <p>If field is not set, i.e. == null, 0 is returned.</p>
1274     *
1275     * @param field To get value for.
1276     *
1277     * @return int value of field or 0 if field is not set.
1278     */

1279    private int getInt(DatatypeConstants.Field JavaDoc field) {
1280        Number JavaDoc n = getField(field);
1281        if (n == null) {
1282            return 0;
1283        } else {
1284            return n.intValue();
1285        }
1286    }
1287        
1288    /**
1289     * <p>Returns the length of the duration in milli-seconds.</p>
1290     *
1291     * <p>If the seconds field carries more digits than milli-second order,
1292     * those will be simply discarded (or in other words, rounded to zero.)
1293     * For example, for any Calendar value <code>x<code>,</p>
1294     * <pre>
1295     * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1296     * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1297     * </pre>
1298     *
1299     * <p>
1300     * Note that this method uses the {@link #addTo(Calendar)} method,
1301     * which may work incorectly with {@link Duration} objects with
1302     * very large values in its fields. See the {@link #addTo(Calendar)}
1303     * method for details.
1304     *
1305     * @param startInstant
1306     * The length of a month/year varies. The <code>startInstant</code> is
1307     * used to disambiguate this variance. Specifically, this method
1308     * returns the difference between <code>startInstant</code> and
1309     * <code>startInstant+duration</code>
1310     *
1311     * @return milliseconds between <code>startInstant</code> and
1312     * <code>startInstant</code> plus this <code>Duration</code>
1313     *
1314     * @throws NullPointerException if <code>startInstant</code> parameter
1315     * is null.
1316     *
1317     */

1318    public long getTimeInMillis(final Calendar JavaDoc startInstant) {
1319        Calendar JavaDoc cal = (Calendar JavaDoc) startInstant.clone();
1320        addTo(cal);
1321        return getCalendarTimeInMillis(cal)
1322                    - getCalendarTimeInMillis(startInstant);
1323    }
1324    
1325    /**
1326     * <p>Returns the length of the duration in milli-seconds.</p>
1327     *
1328     * <p>If the seconds field carries more digits than milli-second order,
1329     * those will be simply discarded (or in other words, rounded to zero.)
1330     * For example, for any <code>Date</code> value <code>x<code>,</p>
1331     * <pre>
1332     * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1333     * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1334     * </pre>
1335     *
1336     * <p>
1337     * Note that this method uses the {@link #addTo(Date)} method,
1338     * which may work incorectly with {@link Duration} objects with
1339     * very large values in its fields. See the {@link #addTo(Date)}
1340     * method for details.
1341     *
1342     * @param startInstant
1343     * The length of a month/year varies. The <code>startInstant</code> is
1344     * used to disambiguate this variance. Specifically, this method
1345     * returns the difference between <code>startInstant</code> and
1346     * <code>startInstant+duration</code>.
1347     *
1348     * @throws NullPointerException
1349     * If the startInstant parameter is null.
1350     *
1351     * @return milliseconds between <code>startInstant</code> and
1352     * <code>startInstant</code> plus this <code>Duration</code>
1353     *
1354     * @see #getTimeInMillis(Calendar)
1355     */

1356    public long getTimeInMillis(final Date JavaDoc startInstant) {
1357        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1358        cal.setTime(startInstant);
1359        this.addTo(cal);
1360        return getCalendarTimeInMillis(cal) - startInstant.getTime();
1361    }
1362    
1363// /**
1364
// * Returns an equivalent but "normalized" duration value.
1365
// *
1366
// * Intuitively, the normalization moves YEARS into
1367
// * MONTHS (by x12) and moves DAYS, HOURS, and MINUTES fields
1368
// * into SECONDS (by x86400, x3600, and x60 respectively.)
1369
// *
1370
// *
1371
// * Formally, this method satisfies the following conditions:
1372
// * <ul>
1373
// * <li>x.normalize().equals(x)
1374
// * <li>!x.normalize().isSet(Duration.YEARS)
1375
// * <li>!x.normalize().isSet(Duration.DAYS)
1376
// * <li>!x.normalize().isSet(Duration.HOURS)
1377
// * <li>!x.normalize().isSet(Duration.MINUTES)
1378
// * </ul>
1379
// *
1380
// * @return
1381
// * always return a non-null valid value.
1382
// */
1383
// public Duration normalize() {
1384
// return null;
1385
// }
1386

1387    /**
1388     * <p>Converts the years and months fields into the days field
1389     * by using a specific time instant as the reference point.</p>
1390     *
1391     * <p>For example, duration of one month normalizes to 31 days
1392     * given the start time instance "July 8th 2003, 17:40:32".</p>
1393     *
1394     * <p>Formally, the computation is done as follows:</p>
1395     * <ol>
1396     * <li>The given Calendar object is cloned.
1397     * <li>The years, months and days fields will be added to
1398     * the {@link Calendar} object
1399     * by using the {@link Calendar#add(int,int)} method.
1400     * <li>The difference between two Calendars are computed in terms of days.
1401     * <li>The computed days, along with the hours, minutes and seconds
1402     * fields of this duration object is used to construct a new
1403     * Duration object.
1404     * </ol>
1405     *
1406     * <p>Note that since the Calendar class uses <code>int</code> to
1407     * hold the value of year and month, this method may produce
1408     * an unexpected result if this duration object holds
1409     * a very large value in the years or months fields.</p>
1410     *
1411     * @param startTimeInstant <code>Calendar</code> reference point.
1412     *
1413     * @return <code>Duration</code> of years and months of this <code>Duration</code> as days.
1414     *
1415     * @throws NullPointerException If the startTimeInstant parameter is null.
1416     */

1417    public Duration JavaDoc normalizeWith(Calendar JavaDoc startTimeInstant) {
1418        
1419        Calendar JavaDoc c = (Calendar JavaDoc) startTimeInstant.clone();
1420        
1421        // using int may cause overflow, but
1422
// Calendar internally treats value as int anyways.
1423
c.add(Calendar.YEAR, getYears() * signum);
1424        c.add(Calendar.MONTH, getMonths() * signum);
1425        c.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1426        
1427        // obtain the difference in terms of days
1428
long diff = getCalendarTimeInMillis(c) - getCalendarTimeInMillis(startTimeInstant);
1429        int days = (int) (diff / (1000L * 60L * 60L * 24L));
1430        
1431        return new DurationImpl(
1432            days >= 0,
1433            null,
1434            null,
1435            wrap(Math.abs(days)),
1436            (BigInteger JavaDoc) getField(DatatypeConstants.HOURS),
1437            (BigInteger JavaDoc) getField(DatatypeConstants.MINUTES),
1438            (BigDecimal JavaDoc) getField(DatatypeConstants.SECONDS));
1439    }
1440    
1441    /**
1442     * <p>Computes a new duration whose value is <code>factor</code> times
1443     * longer than the value of this duration.</p>
1444     *
1445     * <p>This method is provided for the convenience.
1446     * It is functionally equivalent to the following code:</p>
1447     * <pre>
1448     * multiply(new BigDecimal(String.valueOf(factor)))
1449     * </pre>
1450     *
1451     * @param factor Factor times longer of new <code>Duration</code> to create.
1452     *
1453     * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>.
1454     *
1455     * @see #multiply(BigDecimal)
1456     */

1457    public Duration JavaDoc multiply(int factor) {
1458        return multiply(BigDecimal.valueOf(factor));
1459    }
1460    
1461    /**
1462     * Computes a new duration whose value is <code>factor</code> times
1463     * longer than the value of this duration.
1464     *
1465     * <p>
1466     * For example,
1467     * <pre>
1468     * "P1M" (1 month) * "12" = "P12M" (12 months)
1469     * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
1470     * "P1M" (1 month) * "1.5" = IllegalStateException
1471     * </pre>
1472     *
1473     * <p>
1474     * Since the {@link Duration} class is immutable, this method
1475     * doesn't change the value of this object. It simply computes
1476     * a new Duration object and returns it.
1477     *
1478     * <p>
1479     * The operation will be performed field by field with the precision
1480     * of {@link BigDecimal}. Since all the fields except seconds are
1481     * restricted to hold integers,
1482     * any fraction produced by the computation will be
1483     * carried down toward the next lower unit. For example,
1484     * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,
1485     * which will be carried down to "PT12H" (12 hours).
1486     * When fractions of month cannot be meaningfully carried down
1487     * to days, or year to months, this will cause an
1488     * {@link IllegalStateException} to be thrown.
1489     * For example if you multiple one month by 0.5.</p>
1490     *
1491     * <p>
1492     * To avoid {@link IllegalStateException}, use
1493     * the {@link #normalizeWith(Calendar)} method to remove the years
1494     * and months fields.
1495     *
1496     * @param factor to multiply by
1497     *
1498     * @return
1499     * returns a non-null valid {@link Duration} object
1500     *
1501     * @throws IllegalStateException if operation produces fraction in
1502     * the months field.
1503     *
1504     * @throws NullPointerException if the <code>factor</code> parameter is
1505     * <code>null</code>.
1506     *
1507     */

1508    public Duration JavaDoc multiply(BigDecimal JavaDoc factor) {
1509        BigDecimal JavaDoc carry = ZERO;
1510        int factorSign = factor.signum();
1511        factor = factor.abs();
1512        
1513        BigDecimal JavaDoc[] buf = new BigDecimal JavaDoc[6];
1514        
1515        for (int i = 0; i < 5; i++) {
1516            BigDecimal JavaDoc bd = getFieldAsBigDecimal(FIELDS[i]);
1517            bd = bd.multiply(factor).add(carry);
1518            
1519            buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);
1520            
1521            bd = bd.subtract(buf[i]);
1522            if (i == 1) {
1523                if (bd.signum() != 0) {
1524                    throw new IllegalStateException JavaDoc(); // illegal carry-down
1525
} else {
1526                    carry = ZERO;
1527                }
1528            } else {
1529                carry = bd.multiply(FACTORS[i]);
1530            }
1531        }
1532        
1533        if (seconds != null) {
1534            buf[5] = seconds.multiply(factor).add(carry);
1535        } else {
1536            buf[5] = carry;
1537        }
1538                
1539        return new DurationImpl(
1540            this.signum * factorSign >= 0,
1541            toBigInteger(buf[0], null == years),
1542            toBigInteger(buf[1], null == months),
1543            toBigInteger(buf[2], null == days),
1544            toBigInteger(buf[3], null == hours),
1545            toBigInteger(buf[4], null == minutes),
1546            (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
1547    }
1548    
1549    /**
1550     * <p>Gets the value of the field as a {@link BigDecimal}.</p>
1551     *
1552     * <p>If the field is unset, return 0.</p>
1553     *
1554     * @param f Field to get value for.
1555     *
1556     * @return non-null valid {@link BigDecimal}.
1557     */

1558    private BigDecimal JavaDoc getFieldAsBigDecimal(DatatypeConstants.Field JavaDoc f) {
1559        if (f == DatatypeConstants.SECONDS) {
1560            if (seconds != null) {
1561                return seconds;
1562            } else {
1563                return ZERO;
1564            }
1565        } else {
1566            BigInteger JavaDoc bi = (BigInteger JavaDoc) getField(f);
1567            if (bi == null) {
1568                return ZERO;
1569            } else {
1570                return new BigDecimal JavaDoc(bi);
1571            }
1572        }
1573    }
1574    
1575    /**
1576     * <p>BigInteger value of BigDecimal value.</p>
1577     *
1578     * @param value Value to convert.
1579     * @param canBeNull Can returned value be null?
1580     *
1581     * @return BigInteger value of BigDecimal, possibly null.
1582     */

1583    private static BigInteger JavaDoc toBigInteger(
1584        BigDecimal JavaDoc value,
1585        boolean canBeNull) {
1586        if (canBeNull && value.signum() == 0) {
1587            return null;
1588        } else {
1589            return value.unscaledValue();
1590        }
1591    }
1592    
1593    /**
1594     * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
1595     * FIELDS[i+1].
1596     */

1597    private static final BigDecimal JavaDoc[] FACTORS = new BigDecimal JavaDoc[]{
1598        BigDecimal.valueOf(12),
1599        null/*undefined*/,
1600        BigDecimal.valueOf(24),
1601        BigDecimal.valueOf(60),
1602        BigDecimal.valueOf(60)
1603    };
1604    
1605    /**
1606     * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
1607     *
1608     * <p>For example,</p>
1609     * <pre>
1610     * "1 day" + "-3 days" = "-2 days"
1611     * "1 year" + "1 day" = "1 year and 1 day"
1612     * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
1613     * "15 hours" + "-3 days" = "-(2 days,9 hours)"
1614     * "1 year" + "-1 day" = IllegalStateException
1615     * </pre>
1616     *
1617     * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1618     * there are cases where the operation fails in
1619     * {@link IllegalStateException}.</p>
1620     *
1621     * <p>
1622     * Formally, the computation is defined as follows.</p>
1623     * <p>
1624     * Firstly, we can assume that two {@link Duration}s to be added
1625     * are both positive without losing generality (i.e.,
1626     * <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>,
1627     * <code>(-X)+(-Y)=-(X+Y)</code>)
1628     *
1629     * <p>
1630     * Addition of two positive {@link Duration}s are simply defined as
1631     * field by field addition where missing fields are treated as 0.
1632     * <p>
1633     * A field of the resulting {@link Duration} will be unset if and
1634     * only if respective fields of two input {@link Duration}s are unset.
1635     * <p>
1636     * Note that <code>lhs.add(rhs)</code> will be always successful if
1637     * <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are
1638     * normalized.</p>
1639     *
1640     * @param rhs <code>Duration</code> to add to this <code>Duration</code>
1641     *
1642     * @return
1643     * non-null valid Duration object.
1644     *
1645     * @throws NullPointerException
1646     * If the rhs parameter is null.
1647     * @throws IllegalStateException
1648     * If two durations cannot be meaningfully added. For
1649     * example, adding negative one day to one month causes
1650     * this exception.
1651     *
1652     *
1653     * @see #subtract(Duration)
1654     */

1655    public Duration JavaDoc add(final Duration JavaDoc rhs) {
1656        Duration JavaDoc lhs = this;
1657        BigDecimal JavaDoc[] buf = new BigDecimal JavaDoc[6];
1658        
1659        buf[0] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.YEARS),
1660            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.YEARS), rhs.getSign()));
1661        buf[1] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.MONTHS),
1662            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.MONTHS), rhs.getSign()));
1663        buf[2] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.DAYS),
1664            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.DAYS), rhs.getSign()));
1665        buf[3] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.HOURS),
1666            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.HOURS), rhs.getSign()));
1667        buf[4] = sanitize((BigInteger JavaDoc) lhs.getField(DatatypeConstants.MINUTES),
1668            lhs.getSign()).add(sanitize((BigInteger JavaDoc) rhs.getField(DatatypeConstants.MINUTES), rhs.getSign()));
1669        buf[5] = sanitize((BigDecimal JavaDoc) lhs.getField(DatatypeConstants.SECONDS),
1670            lhs.getSign()).add(sanitize((BigDecimal JavaDoc) rhs.getField(DatatypeConstants.SECONDS), rhs.getSign()));
1671        
1672        // align sign
1673
alignSigns(buf, 0, 2); // Y,M
1674
alignSigns(buf, 2, 6); // D,h,m,s
1675

1676        // make sure that the sign bit is consistent across all 6 fields.
1677
int s = 0;
1678        for (int i = 0; i < 6; i++) {
1679            if (s * buf[i].signum() < 0) {
1680                throw new IllegalStateException JavaDoc();
1681            }
1682            if (s == 0) {
1683                s = buf[i].signum();
1684            }
1685        }
1686        
1687        return new DurationImpl(
1688            s >= 0,
1689            toBigInteger(sanitize(buf[0], s),
1690                lhs.getField(DatatypeConstants.YEARS) == null && rhs.getField(DatatypeConstants.YEARS) == null),
1691            toBigInteger(sanitize(buf[1], s),
1692                lhs.getField(DatatypeConstants.MONTHS) == null && rhs.getField(DatatypeConstants.MONTHS) == null),
1693            toBigInteger(sanitize(buf[2], s),
1694                lhs.getField(DatatypeConstants.DAYS) == null && rhs.getField(DatatypeConstants.DAYS) == null),
1695            toBigInteger(sanitize(buf[3], s),
1696                lhs.getField(DatatypeConstants.HOURS) == null && rhs.getField(DatatypeConstants.HOURS) == null),
1697            toBigInteger(sanitize(buf[4], s),
1698                lhs.getField(DatatypeConstants.MINUTES) == null && rhs.getField(DatatypeConstants.MINUTES) == null),
1699             (buf[5].signum() == 0
1700             && lhs.getField(DatatypeConstants.SECONDS) == null
1701             && rhs.getField(DatatypeConstants.SECONDS) == null) ? null : sanitize(buf[5], s));
1702    }
1703    
1704    private static void alignSigns(BigDecimal JavaDoc[] buf, int start, int end) {
1705        // align sign
1706
boolean touched;
1707        
1708        do { // repeat until all the sign bits become consistent
1709
touched = false;
1710            int s = 0; // sign of the left fields
1711

1712            for (int i = start; i < end; i++) {
1713                if (s * buf[i].signum() < 0) {
1714                    // this field has different sign than its left field.
1715
touched = true;
1716
1717                    // compute the number of unit that needs to be borrowed.
1718
BigDecimal JavaDoc borrow =
1719                        buf[i].abs().divide(
1720                            FACTORS[i - 1],
1721                            BigDecimal.ROUND_UP);
1722                    if (buf[i].signum() > 0) {
1723                        borrow = borrow.negate();
1724                    }
1725
1726                    // update values
1727
buf[i - 1] = buf[i - 1].subtract(borrow);
1728                    buf[i] = buf[i].add(borrow.multiply(FACTORS[i - 1]));
1729                }
1730                if (buf[i].signum() != 0) {
1731                    s = buf[i].signum();
1732                }
1733            }
1734        } while (touched);
1735    }
1736    
1737    /**
1738     * Compute <code>value*signum</code> where value==null is treated as
1739     * value==0.
1740     * @param value Value to sanitize.
1741     * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1742     *
1743     * @return non-null {@link BigDecimal}.
1744     */

1745    private static BigDecimal JavaDoc sanitize(BigInteger JavaDoc value, int signum) {
1746        if (signum == 0 || value == null) {
1747            return ZERO;
1748        }
1749        if (signum > 0) {
1750            return new BigDecimal JavaDoc(value);
1751        }
1752        return new BigDecimal JavaDoc(value.negate());
1753    }
1754        
1755    /**
1756     * <p>Compute <code>value*signum</code> where <code>value==null</code> is treated as <code>value==0</code></p>.
1757     *
1758     * @param value Value to sanitize.
1759     * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1760     *
1761     * @return non-null {@link BigDecimal}.
1762     */

1763    static BigDecimal JavaDoc sanitize(BigDecimal JavaDoc value, int signum) {
1764        if (signum == 0 || value == null) {
1765            return ZERO;
1766        }
1767        if (signum > 0) {
1768            return value;
1769        }
1770        return value.negate();
1771    }
1772    
1773    /**
1774     * <p>Computes a new duration whose value is <code>this-rhs</code>.</p>
1775     *
1776     * <p>For example:</p>
1777     * <pre>
1778     * "1 day" - "-3 days" = "4 days"
1779     * "1 year" - "1 day" = IllegalStateException
1780     * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)"
1781     * "15 hours" - "-3 days" = "3 days and 15 hours"
1782     * "1 year" - "-1 day" = "1 year and 1 day"
1783     * </pre>
1784     *
1785     * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1786     * there are cases where the operation fails in {@link IllegalStateException}.</p>
1787     *
1788     * <p>Formally the computation is defined as follows.
1789     * First, we can assume that two {@link Duration}s are both positive
1790     * without losing generality. (i.e.,
1791     * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>,
1792     * <code>(-X)-(-Y)=-(X-Y)</code>)</p>
1793     *
1794     * <p>Then two durations are subtracted field by field.
1795     * If the sign of any non-zero field <tt>F</tt> is different from
1796     * the sign of the most significant field,
1797     * 1 (if <tt>F</tt> is negative) or -1 (otherwise)
1798     * will be borrowed from the next bigger unit of <tt>F</tt>.</p>
1799     *
1800     * <p>This process is repeated until all the non-zero fields have
1801     * the same sign.</p>
1802     *
1803     * <p>If a borrow occurs in the days field (in other words, if
1804     * the computation needs to borrow 1 or -1 month to compensate
1805     * days), then the computation fails by throwing an
1806     * {@link IllegalStateException}.</p>
1807     *
1808     * @param rhs <code>Duration</code> to substract from this <code>Duration</code>.
1809     *
1810     * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>.
1811     *
1812     * @throws IllegalStateException
1813     * If two durations cannot be meaningfully subtracted. For
1814     * example, subtracting one day from one month causes
1815     * this exception.
1816     *
1817     * @throws NullPointerException
1818     * If the rhs parameter is null.
1819     *
1820     * @see #add(Duration)
1821     */

1822    public Duration JavaDoc subtract(final Duration JavaDoc rhs) {
1823        return add(rhs.negate());
1824    }
1825    
1826    /**
1827     * Returns a new {@link Duration} object whose
1828     * value is <code>-this</code>.
1829     *
1830     * <p>
1831     * Since the {@link Duration} class is immutable, this method
1832     * doesn't change the value of this object. It simply computes
1833     * a new Duration object and returns it.
1834     *
1835     * @return
1836     * always return a non-null valid {@link Duration} object.
1837     */

1838    public Duration JavaDoc negate() {
1839        return new DurationImpl(
1840            signum <= 0,
1841            years,
1842            months,
1843            days,
1844            hours,
1845            minutes,
1846            seconds);
1847    }
1848    
1849    /**
1850     * Returns the sign of this duration in -1,0, or 1.
1851     *
1852     * @return
1853     * -1 if this duration is negative, 0 if the duration is zero,
1854     * and 1 if the duration is postive.
1855     */

1856    public int signum() {
1857        return signum;
1858    }
1859    
1860    
1861    /**
1862     * Adds this duration to a {@link Calendar} object.
1863     *
1864     * <p>
1865     * Calls {@link java.util.Calendar#add(int,int)} in the
1866     * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS
1867     * if those fields are present. Because the {@link Calendar} class
1868     * uses int to hold values, there are cases where this method
1869     * won't work correctly (for example if values of fields
1870     * exceed the range of int.)
1871     * </p>
1872     *
1873     * <p>
1874     * Also, since this duration class is a Gregorian duration, this
1875     * method will not work correctly if the given {@link Calendar}
1876     * object is based on some other calendar systems.
1877     * </p>
1878     *
1879     * <p>
1880     * Any fractional parts of this {@link Duration} object
1881     * beyond milliseconds will be simply ignored. For example, if
1882     * this duration is "P1.23456S", then 1 is added to SECONDS,
1883     * 234 is added to MILLISECONDS, and the rest will be unused.
1884     * </p>
1885     *
1886     * <p>
1887     * Note that because {@link Calendar#add(int, int)} is using
1888     * <tt>int</tt>, {@link Duration} with values beyond the
1889     * range of <tt>int</tt> in its fields
1890     * will cause overflow/underflow to the given {@link Calendar}.
1891     * {@link XMLGregorianCalendar#add(Duration)} provides the same
1892     * basic operation as this method while avoiding
1893     * the overflow/underflow issues.
1894     *
1895     * @param calendar
1896     * A calendar object whose value will be modified.
1897     * @throws NullPointerException
1898     * if the calendar parameter is null.
1899     */

1900    public void addTo(Calendar JavaDoc calendar) {
1901        calendar.add(Calendar.YEAR, getYears() * signum);
1902        calendar.add(Calendar.MONTH, getMonths() * signum);
1903        calendar.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1904        calendar.add(Calendar.HOUR, getHours() * signum);
1905        calendar.add(Calendar.MINUTE, getMinutes() * signum);
1906        calendar.add(Calendar.SECOND, getSeconds() * signum);
1907
1908        if (seconds != null) {
1909            BigDecimal JavaDoc fraction =
1910                seconds.subtract(seconds.setScale(0, BigDecimal.ROUND_DOWN));
1911            int millisec = fraction.movePointRight(3).intValue();
1912            calendar.add(Calendar.MILLISECOND, millisec * signum);
1913        }
1914    }
1915    
1916    /**
1917     * Adds this duration to a {@link Date} object.
1918     *
1919     * <p>
1920     * The given date is first converted into
1921     * a {@link java.util.GregorianCalendar}, then the duration
1922     * is added exactly like the {@link #addTo(Calendar)} method.
1923     *
1924     * <p>
1925     * The updated time instant is then converted back into a
1926     * {@link Date} object and used to update the given {@link Date} object.
1927     *
1928     * <p>
1929     * This somewhat redundant computation is necessary to unambiguously
1930     * determine the duration of months and years.
1931     *
1932     * @param date
1933     * A date object whose value will be modified.
1934     * @throws NullPointerException
1935     * if the date parameter is null.
1936     */

1937    public void addTo(Date JavaDoc date) {
1938        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1939        cal.setTime(date); // this will throw NPE if date==null
1940
this.addTo(cal);
1941        date.setTime(getCalendarTimeInMillis(cal));
1942    }
1943    
1944    /**
1945     * <p>Stream Unique Identifier.</p>
1946     *
1947     * <p>TODO: Serialization should use the XML string representation as
1948     * the serialization format to ensure future compatibility.</p>
1949     */

1950    private static final long serialVersionUID = 1L;
1951    
1952    /**
1953     * Writes {@link Duration} as a lexical representation
1954     * for maximum future compatibility.
1955     *
1956     * @return
1957     * An object that encapsulates the string
1958     * returned by <code>this.toString()</code>.
1959     */

1960    private Object JavaDoc writeReplace() throws IOException JavaDoc {
1961        return new DurationStream(this.toString());
1962    }
1963    
1964    /**
1965     * Representation of {@link Duration} in the object stream.
1966     *
1967     * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
1968     */

1969    private static class DurationStream implements Serializable JavaDoc {
1970        private final String JavaDoc lexical;
1971
1972        private DurationStream(String JavaDoc _lexical) {
1973            this.lexical = _lexical;
1974        }
1975        
1976        private Object JavaDoc readResolve() throws ObjectStreamException JavaDoc {
1977            // try {
1978
return new DurationImpl(lexical);
1979            // } catch( ParseException e ) {
1980
// throw new StreamCorruptedException("unable to parse "+lexical+" as duration");
1981
// }
1982
}
1983        
1984        private static final long serialVersionUID = 1L;
1985    }
1986    
1987    /**
1988     * Calls the {@link Calendar#getTimeInMillis} method.
1989     * Prior to JDK1.4, this method was protected and therefore
1990     * cannot be invoked directly.
1991     *
1992     * In future, this should be replaced by
1993     * <code>cal.getTimeInMillis()</code>
1994     */

1995    private static long getCalendarTimeInMillis(Calendar JavaDoc cal) {
1996        return cal.getTime().getTime();
1997    }
1998}
1999
2000
Popular Tags