1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.jaxp.datatype;
  22 
  23 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
  24 import java.io.IOException;
  25 import java.io.ObjectStreamException;
  26 import java.io.Serializable;
  27 import java.math.BigDecimal;
  28 import java.math.BigInteger;
  29 import java.math.RoundingMode;
  30 import java.util.Calendar;
  31 import java.util.Date;
  32 import java.util.GregorianCalendar;
  33 import java.util.TimeZone;
  34 import javax.xml.datatype.DatatypeConstants;
  35 import javax.xml.datatype.Duration;
  36 import javax.xml.datatype.XMLGregorianCalendar;
  37 
  38 /**
  39  * <p>Immutable representation of a time span as defined in
  40  * the W3C XML Schema 1.0 specification.</p>
  41  *
  42  * <p>A Duration object represents a period of Gregorian time,
  43  * which consists of six fields (years, months, days, hours,
  44  * minutes, and seconds) plus a sign (+/-) field.</p>
  45  *
  46  * <p>The first five fields have non-negative (>=0) integers or null
  47  * (which represents that the field is not set),
  48  * and the seconds field has a non-negative decimal or null.
  49  * A negative sign indicates a negative duration.</p>
  50  *
  51  * <p>This class provides a number of methods that make it easy
  52  * to use for the duration datatype of XML Schema 1.0 with
  53  * the errata.</p>
  54  *
  55  * <h2>Order relationship</h2>
  56  * <p>Duration objects only have partial order, where two values A and B
  57  * maybe either:</p>
  58  * <ol>
  59  *  <li>A&lt;B (A is shorter than B)
  60  *  <li>A&gt;B (A is longer than B)
  61  *  <li>A==B   (A and B are of the same duration)
  62  *  <li>A&lt;>B (Comparison between A and B is indeterminate)
  63  * </ol>
  64  * <p>For example, 30 days cannot be meaningfully compared to one month.
  65  * The {@link #compare(Duration)} method implements this
  66  * relationship.</p>
  67  *
  68  * <p>See the {@link #isLongerThan(Duration)} method for details about
  69  * the order relationship among {@link Duration} objects.</p>
  70  *
  71  *
  72  *
  73  * <h2>Operations over Duration</h2>
  74  * <p>This class provides a set of basic arithmetic operations, such
  75  * as addition, subtraction and multiplication.
  76  * Because durations don't have total order, an operation could
  77  * fail for some combinations of operations. For example, you cannot
  78  * subtract 15 days from 1 month. See the javadoc of those methods
  79  * for detailed conditions where this could happen.</p>
  80  *
  81  * <p>Also, division of a duration by a number is not provided because
  82  * the {@link Duration} class can only deal with finite precision
  83  * decimal numbers. For example, one cannot represent 1 sec divided by 3.</p>
  84  *
  85  * <p>However, you could substitute a division by 3 with multiplying
  86  * by numbers such as 0.3 or 0.333.</p>
  87  *
  88  *
  89  *
  90  * <h2>Range of allowed values</h2>
  91  * <p>
  92  * Because some operations of {@link Duration} rely on {@link Calendar}
  93  * even though {@link Duration} can hold very large or very small values,
  94  * some of the methods may not work correctly on such {@link Duration}s.
  95  * The impacted methods document their dependency on {@link Calendar}.
  96  *
  97  *
  98  * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
  99  * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
 100 
 101  * @see XMLGregorianCalendar#add(Duration)
 102  */
 103 class DurationImpl
 104         extends Duration
 105         implements Serializable {
 106 
 107 
 108     /**
 109      * <p>Internal array of value Fields.</p>
 110      */
 111     private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
 112         DatatypeConstants.YEARS,
 113         DatatypeConstants.MONTHS,
 114         DatatypeConstants.DAYS,
 115         DatatypeConstants.HOURS,
 116         DatatypeConstants.MINUTES,
 117         DatatypeConstants.SECONDS
 118     };
 119 
 120 
 121     /**
 122      * TimeZone for GMT.
 123      */
 124     private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
 125 
 126     /**
 127      * <p>BigDecimal value of 0.</p>
 128      */
 129     private static final BigDecimal ZERO = BigDecimal.valueOf(0);
 130 
 131     /**
 132      * BigInteger value of Integer's max value.</p>
 133      */
 134     private static final BigInteger MaxIntAsBigInt =
 135             BigInteger.valueOf((long) Integer.MAX_VALUE);
 136 
 137     /**
 138      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
 139      * zero, or positive.</p>
 140      */
 141     protected int signum;
 142 
 143     /**
 144      * <p>Years of this <code>Duration</code>.</p>
 145      */
 146     /**
 147      * These were final since Duration is immutable. But new subclasses need
 148      * to be able to set after conversion. It won't break the immutable nature
 149      * of them since there's no other way to set new values to them
 150      */
 151     protected BigInteger years;
 152 
 153     /**
 154      * <p>Months of this <code>Duration</code>.</p>
 155      */
 156     protected BigInteger months;
 157 
 158     /**
 159      * <p>Days of this <code>Duration</code>.</p>
 160      */
 161     protected BigInteger days;
 162 
 163     /**
 164      * <p>Hours of this <code>Duration</code>.</p>
 165      */
 166     protected BigInteger hours;
 167 
 168     /**
 169      * <p>Minutes of this <code>Duration</code>.</p>
 170      */
 171     protected BigInteger minutes;
 172 
 173     /**
 174      * <p>Seconds of this <code>Duration</code>.</p>
 175      */
 176     protected BigDecimal seconds;
 177 
 178     /**
 179      * Returns the sign of this duration in -1,0, or 1.
 180      *
 181      * @return
 182      *      -1 if this duration is negative, 0 if the duration is zero,
 183      *      and 1 if the duration is postive.
 184      */
 185     public int getSign() {
 186 
 187         return signum;
 188     }
 189 
 190     /**
 191      * Determine the sign of the duration.
 192      *
 193      * @param isPositive Sign.
 194      * @return 1 if positive, -1 negative, or 0 if all fields are zero.
 195      */
 196     protected int calcSignum(boolean isPositive) {
 197         if ((years == null || years.signum() == 0)
 198              && (months == null || months.signum() == 0)
 199              && (days == null || days.signum() == 0)
 200              && (hours == null || hours.signum() == 0)
 201              && (minutes == null || minutes.signum() == 0)
 202              && (seconds == null || seconds.signum() == 0)) {
 203             return 0;
 204         }
 205 
 206         if (isPositive) {
 207             return 1;
 208         }
 209         else {
 210             return -1;
 211         }
 212     }
 213 
 214     /**
 215      * <p>Constructs a new Duration object by specifying each field individually.</p>
 216      *
 217      * <p>All the parameters are optional as long as at least one field is present.
 218      * If specified, parameters have to be zero or positive.</p>
 219      *
 220      * @param isPositive Set to <code>false</code> to create a negative duration. When the length
 221      *   of the duration is zero, this parameter will be ignored.
 222      * @param years of this <code>Duration</code>
 223      * @param months of this <code>Duration</code>
 224      * @param days of this <code>Duration</code>
 225      * @param hours of this <code>Duration</code>
 226      * @param minutes of this <code>Duration</code>
 227      * @param seconds of this <code>Duration</code>
 228      *
 229      * @throws IllegalArgumentException
 230      *    If years, months, days, hours, minutes and
 231      *    seconds parameters are all <code>null</code>. Or if any
 232      *    of those parameters are negative.
 233      */
 234     protected DurationImpl(
 235         boolean isPositive,
 236         BigInteger years,
 237         BigInteger months,
 238         BigInteger days,
 239         BigInteger hours,
 240         BigInteger minutes,
 241         BigDecimal seconds) {
 242 
 243         this.years = years;
 244         this.months = months;
 245         this.days = days;
 246         this.hours = hours;
 247         this.minutes = minutes;
 248         this.seconds = seconds;
 249 
 250         this.signum = calcSignum(isPositive);
 251 
 252         // sanity check
 253         if (years == null
 254             && months == null
 255             && days == null
 256             && hours == null
 257             && minutes == null
 258             && seconds == null) {
 259             throw new IllegalArgumentException(
 260             //"all the fields are null"
 261             DatatypeMessageFormatter.formatMessage(null, "AllFieldsNull", null)
 262             );
 263         }
 264         testNonNegative(years, DatatypeConstants.YEARS);
 265         testNonNegative(months, DatatypeConstants.MONTHS);
 266         testNonNegative(days, DatatypeConstants.DAYS);
 267         testNonNegative(hours, DatatypeConstants.HOURS);
 268         testNonNegative(minutes, DatatypeConstants.MINUTES);
 269         testNonNegative(seconds, DatatypeConstants.SECONDS);
 270     }
 271 
 272     /**
 273      * <p>Makes sure that the given number is non-negative. If it is not,
 274      * throw {@link IllegalArgumentException}.</p>
 275      *
 276      * @param n Number to test.
 277      * @param f Field to test.
 278      */
 279     protected static void testNonNegative(BigInteger n, DatatypeConstants.Field f) {
 280         if (n != null && n.signum() < 0) {
 281             throw new IllegalArgumentException(
 282                 DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object[]{f.toString()})
 283             );
 284         }
 285     }
 286 
 287     /**
 288      * <p>Makes sure that the given number is non-negative. If it is not,
 289      * throw {@link IllegalArgumentException}.</p>
 290      *
 291      * @param n Number to test.
 292      * @param f Field to test.
 293      */
 294     protected static void testNonNegative(BigDecimal n, DatatypeConstants.Field f) {
 295         if (n != null && n.signum() < 0) {
 296 
 297             throw new IllegalArgumentException(
 298                 DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object[]{f.toString()})
 299             );
 300         }
 301     }
 302 
 303     /**
 304      * <p>Constructs a new Duration object by specifying each field
 305      * individually.</p>
 306      *
 307      * <p>This method is functionally equivalent to
 308      * invoking another constructor by wrapping
 309      * all non-zero parameters into {@link BigInteger} and {@link BigDecimal}.
 310      * Zero value of int parameter is equivalent of null value of
 311      * the corresponding field.</p>
 312      *
 313      * @see #DurationImpl(boolean, BigInteger, BigInteger, BigInteger, BigInteger,
 314      *   BigInteger, BigDecimal)
 315      */
 316     protected DurationImpl(
 317         final boolean isPositive,
 318         final int years,
 319         final int months,
 320         final int days,
 321         final int hours,
 322         final int minutes,
 323         final int seconds) {
 324         this(
 325             isPositive,
 326             wrap(years),
 327             wrap(months),
 328             wrap(days),
 329             wrap(hours),
 330             wrap(minutes),
 331             seconds != DatatypeConstants.FIELD_UNDEFINED ? new BigDecimal(String.valueOf(seconds)) : null);
 332     }
 333 
 334         /**
 335          * TODO: Javadoc
 336          *
 337          * @param i int to convert to BigInteger.
 338          *
 339          * @return BigInteger representation of int.
 340          */
 341     protected static BigInteger wrap(final int i) {
 342 
 343         // field may not be set
 344         if (i == DatatypeConstants.FIELD_UNDEFINED) {
 345                 return null;
 346         }
 347 
 348         // int -> BigInteger
 349         return BigInteger.valueOf(i);
 350     }
 351 
 352     /**
 353      * <p>Constructs a new Duration object by specifying the duration
 354      * in milliseconds.</p>
 355      *
 356      * @param durationInMilliSeconds
 357      *      The length of the duration in milliseconds.
 358      */
 359     protected DurationImpl(final long durationInMilliSeconds) {
 360         boolean is0x8000000000000000L = false;
 361         long l = durationInMilliSeconds;
 362 
 363         if (l > 0) {
 364             signum = 1;
 365         }
 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         }
 375         else {
 376             signum = 0;
 377         }
 378 
 379         // let GregorianCalendar do the heavy lifting
 380         GregorianCalendar gregorianCalendar = new GregorianCalendar(GMT);
 381 
 382         // duration is the offset from the Epoch
 383         gregorianCalendar.setTimeInMillis(l);
 384 
 385         // now find out how much each field has changed
 386         long int2long = 0L;
 387 
 388         // years
 389         int2long = gregorianCalendar.get(Calendar.YEAR) - 1970;
 390         this.years = BigInteger.valueOf(int2long);
 391 
 392         // months
 393         int2long = gregorianCalendar.get(Calendar.MONTH);
 394         this.months = BigInteger.valueOf(int2long);
 395 
 396         // days
 397         int2long = gregorianCalendar.get(Calendar.DAY_OF_MONTH) - 1;
 398         this.days = BigInteger.valueOf(int2long);
 399 
 400         // hours
 401         int2long = gregorianCalendar.get(Calendar.HOUR_OF_DAY);
 402         this.hours = BigInteger.valueOf(int2long);
 403 
 404         // minutes
 405         int2long = gregorianCalendar.get(Calendar.MINUTE);
 406         this.minutes = BigInteger.valueOf(int2long);
 407 
 408         // seconds & milliseconds
 409         int2long = (gregorianCalendar.get(Calendar.SECOND) * 1000)
 410                     + gregorianCalendar.get(Calendar.MILLISECOND)
 411                     + (is0x8000000000000000L ? 1 : 0);
 412         this.seconds = BigDecimal.valueOf(int2long, 3);
 413     }
 414 
 415     /**
 416      * Constructs a new Duration object by
 417      * parsing its string representation
 418      * "PnYnMnDTnHnMnS" as defined in XML Schema 1.0 section 3.2.6.1.
 419      *
 420      * <p>
 421      * The string representation may not have any leading
 422      * and trailing whitespaces.
 423      *
 424      * <p>
 425      * For example, this method parses strings like
 426      * "P1D" (1 day), "-PT100S" (-100 sec.), "P1DT12H" (1 days and 12 hours).
 427      *
 428      * <p>
 429      * The parsing is done field by field so that
 430      * the following holds for any lexically correct string x:
 431      * <pre>
 432      * new Duration(x).toString().equals(x)
 433      * </pre>
 434      *
 435      * Returns a non-null valid duration object that holds the value
 436      * indicated by the lexicalRepresentation parameter.
 437      *
 438      * @param lexicalRepresentation
 439      *      Lexical representation of a duration.
 440      * @throws IllegalArgumentException
 441      *      If the given string does not conform to the aforementioned
 442      *      specification.
 443      * @throws NullPointerException
 444      *      If the given string is null.
 445      */
 446     protected DurationImpl(String lexicalRepresentation)
 447         throws IllegalArgumentException {
 448         // only if I could use the JDK1.4 regular expression ....
 449 
 450         if (lexicalRepresentation == null) {
 451            throw new NullPointerException();
 452         }
 453 
 454         final String s = lexicalRepresentation;
 455         boolean positive;
 456         int[] idx = new int[1];
 457         int length = s.length();
 458         boolean timeRequired = false;
 459 
 460         idx[0] = 0;
 461         if (length != idx[0] && s.charAt(idx[0]) == '-') {
 462             idx[0]++;
 463             positive = false;
 464         }
 465         else {
 466             positive = true;
 467         }
 468 
 469         if (length != idx[0] && s.charAt(idx[0]++) != 'P') {
 470             throw new IllegalArgumentException(s); //,idx[0]-1);
 471         }
 472 
 473 
 474         // phase 1: chop the string into chunks
 475         // (where a chunk is '<number><a symbol>'
 476         //--------------------------------------
 477         int dateLen = 0;
 478         String[] dateParts = new String[3];
 479         int[] datePartsIndex = new int[3];
 480         while (length != idx[0]
 481                && isDigit(s.charAt(idx[0]))
 482                && dateLen < 3) {
 483             datePartsIndex[dateLen] = idx[0];
 484             dateParts[dateLen++] = parsePiece(s, idx);
 485         }
 486 
 487         if (length != idx[0]) {
 488             if (s.charAt(idx[0]++) == 'T') {
 489                 timeRequired = true;
 490             }
 491             else {
 492                 throw new IllegalArgumentException(s); // ,idx[0]-1);
 493             }
 494         }
 495 
 496         int timeLen = 0;
 497         String[] timeParts = new String[3];
 498         int[] timePartsIndex = new int[3];
 499         while (length != idx[0]
 500                              && isDigitOrPeriod(s.charAt(idx[0]))
 501                              && timeLen < 3) {
 502             timePartsIndex[timeLen] = idx[0];
 503             timeParts[timeLen++] = parsePiece(s, idx);
 504         }
 505 
 506         if (timeRequired && timeLen == 0) {
 507             throw new IllegalArgumentException(s); // ,idx[0]);
 508         }
 509 
 510         if (length != idx[0]) {
 511             throw new IllegalArgumentException(s); // ,idx[0]);
 512         }
 513         if (dateLen == 0 && timeLen == 0) {
 514             throw new IllegalArgumentException(s); // ,idx[0]);
 515         }
 516 
 517         // phase 2: check the ordering of chunks
 518         //--------------------------------------
 519         organizeParts(s, dateParts, datePartsIndex, dateLen, "YMD");
 520         organizeParts(s, timeParts, timePartsIndex, timeLen, "HMS");
 521 
 522         // parse into numbers
 523         years = parseBigInteger(s, dateParts[0], datePartsIndex[0]);
 524         months = parseBigInteger(s, dateParts[1], datePartsIndex[1]);
 525         days = parseBigInteger(s, dateParts[2], datePartsIndex[2]);
 526         hours = parseBigInteger(s, timeParts[0], timePartsIndex[0]);
 527         minutes = parseBigInteger(s, timeParts[1], timePartsIndex[1]);
 528         seconds = parseBigDecimal(s, timeParts[2], timePartsIndex[2]);
 529         signum = calcSignum(positive);
 530     }
 531 
 532 
 533     /**
 534      * TODO: Javadoc
 535      *
 536      * @param ch char to test.
 537      *
 538      * @return true if ch is a digit, else false.
 539      */
 540     private static boolean isDigit(char ch) {
 541         return '0' <= ch && ch <= '9';
 542     }
 543 
 544     /**
 545      * TODO: Javadoc
 546      *
 547      * @param ch to test.
 548      *
 549      * @return true if ch is a digit or a period, else false.
 550      */
 551     private static boolean isDigitOrPeriod(char ch) {
 552         return isDigit(ch) || ch == '.';
 553     }
 554 
 555     /**
 556      * TODO: Javadoc
 557      *
 558      * @param whole String to parse.
 559      * @param idx TODO: ???
 560      *
 561      * @return Result of parsing.
 562      *
 563      * @throws IllegalArgumentException If whole cannot be parsed.
 564      */
 565     private static String parsePiece(String whole, int[] idx)
 566         throws IllegalArgumentException {
 567         int start = idx[0];
 568         while (idx[0] < whole.length()
 569             && isDigitOrPeriod(whole.charAt(idx[0]))) {
 570             idx[0]++;
 571             }
 572         if (idx[0] == whole.length()) {
 573             throw new IllegalArgumentException(whole); // ,idx[0]);
 574         }
 575 
 576         idx[0]++;
 577 
 578         return whole.substring(start, idx[0]);
 579     }
 580 
 581     /**
 582      * TODO: Javadoc.
 583      *
 584      * @param whole TODO: ???
 585      * @param parts TODO: ???
 586      * @param partsIndex TODO: ???
 587      * @param len TODO: ???
 588      * @param tokens TODO: ???
 589      *
 590      * @throws IllegalArgumentException TODO: ???
 591      */
 592     private static void organizeParts(
 593         String whole,
 594         String[] parts,
 595         int[] partsIndex,
 596         int len,
 597         String tokens)
 598         throws IllegalArgumentException {
 599 
 600         int idx = tokens.length();
 601         for (int i = len - 1; i >= 0; i--) {
 602             if (parts[i] == null) {
 603                 throw new IllegalArgumentException(whole);
 604             }
 605             int nidx =
 606                 tokens.lastIndexOf(
 607                     parts[i].charAt(parts[i].length() - 1),
 608                     idx - 1);
 609             if (nidx == -1) {
 610                 throw new IllegalArgumentException(whole);
 611                 // ,partsIndex[i]+parts[i].length()-1);
 612             }
 613 
 614             for (int j = nidx + 1; j < idx; j++) {
 615                 parts[j] = null;
 616             }
 617             idx = nidx;
 618             parts[idx] = parts[i];
 619             partsIndex[idx] = partsIndex[i];
 620         }
 621         for (idx--; idx >= 0; idx--) {
 622             parts[idx] = null;
 623         }
 624     }
 625 
 626     /**
 627      * TODO: Javadoc
 628      *
 629      * @param whole TODO: ???.
 630      * @param part TODO: ???.
 631      * @param index TODO: ???.
 632      *
 633      * @return TODO: ???.
 634      *
 635      * @throws IllegalArgumentException TODO: ???.
 636      */
 637     private static BigInteger parseBigInteger(
 638         String whole,
 639         String part,
 640         int index)
 641         throws IllegalArgumentException {
 642         if (part == null) {
 643             return null;
 644         }
 645         part = part.substring(0, part.length() - 1);
 646         //        try {
 647         return new BigInteger(part);
 648         //        } catch( NumberFormatException e ) {
 649         //            throw new ParseException( whole, index );
 650         //        }
 651     }
 652 
 653     /**
 654      * TODO: Javadoc.
 655      *
 656      * @param whole TODO: ???.
 657      * @param part TODO: ???.
 658      * @param index TODO: ???.
 659      *
 660      * @return TODO: ???.
 661      *
 662      * @throws IllegalArgumentException TODO: ???.
 663      */
 664     private static BigDecimal parseBigDecimal(
 665         String whole,
 666         String part,
 667         int index)
 668         throws IllegalArgumentException {
 669         if (part == null) {
 670             return null;
 671         }
 672         part = part.substring(0, part.length() - 1);
 673         // NumberFormatException is IllegalArgumentException
 674         //        try {
 675         return new BigDecimal(part);
 676         //        } catch( NumberFormatException e ) {
 677         //            throw new ParseException( whole, index );
 678         //        }
 679     }
 680 
 681     /**
 682      * <p>Four constants defined for the comparison of durations.</p>
 683      */
 684     private static final XMLGregorianCalendar[] TEST_POINTS = new XMLGregorianCalendar[] {
 685         XMLGregorianCalendarImpl.parse("1696-09-01T00:00:00Z"),
 686         XMLGregorianCalendarImpl.parse("1697-02-01T00:00:00Z"),
 687         XMLGregorianCalendarImpl.parse("1903-03-01T00:00:00Z"),
 688         XMLGregorianCalendarImpl.parse("1903-07-01T00:00:00Z")
 689     };
 690 
 691     /**
 692      * <p>Partial order relation comparison with this <code>Duration</code> instance.</p>
 693      *
 694      * <p>Comparison result must be in accordance with
 695      * <a href="http://www.w3.org/TR/xmlschema-2/#duration-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2,
 696      * <i>Order relation on duration</i></a>.</p>
 697      *
 698      * <p>Return:</p>
 699      * <ul>
 700      *   <li>{@link DatatypeConstants#LESSER} if this <code>Duration</code> is shorter than <code>duration</code> parameter</li>
 701      *   <li>{@link DatatypeConstants#EQUAL} if this <code>Duration</code> is equal to <code>duration</code> parameter</li>
 702      *   <li>{@link DatatypeConstants#GREATER} if this <code>Duration</code> is longer than <code>duration</code> parameter</li>
 703      *   <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li>
 704      * </ul>
 705      *
 706      * @param duration to compare
 707      *
 708      * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
 709      *   {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER}
 710      *   or {@link DatatypeConstants#INDETERMINATE}.
 711      *
 712      * @throws UnsupportedOperationException If the underlying implementation
 713      *   cannot reasonably process the request, e.g. W3C XML Schema allows for
 714      *   arbitrarily large/small/precise values, the request may be beyond the
 715      *   implementations capability.
 716      * @throws NullPointerException if <code>duration</code> is <code>null</code>.
 717      *
 718      * @see #isShorterThan(Duration)
 719      * @see #isLongerThan(Duration)
 720      */
 721     public int compare(Duration rhs) {
 722         /** check if any field in the Durations is too large for the operation
 723          * that uses XMLGregorianCalendar for comparison
 724         */
 725         for (DatatypeConstants.Field field : FIELDS) {
 726             checkMaxValue(getField(field), field);
 727             checkMaxValue(rhs.getField(field), field);
 728         }
 729 
 730         return compareDates(this, rhs);
 731     }
 732 
 733     /**
 734      * Check if a field exceeds the maximum value
 735      * @param field the value of a field
 736      * @param fieldType type of the field, e.g. year, month, day, hour, minute or second.
 737      */
 738     private void checkMaxValue(Number field, DatatypeConstants.Field fieldType) {
 739         BigInteger fieldValue = null;
 740         if (fieldType != DatatypeConstants.SECONDS) {
 741             fieldValue = (BigInteger) field;
 742         } else {
 743             BigDecimal rhsSecondsAsBigDecimal = (BigDecimal) field;
 744             if ( rhsSecondsAsBigDecimal != null ) {
 745                 fieldValue =  rhsSecondsAsBigDecimal.toBigInteger();
 746             }
 747         }
 748 
 749         if (fieldValue != null && fieldValue.compareTo(MaxIntAsBigInt) == 1) {
 750             throw new UnsupportedOperationException(
 751                     DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 752                     new Object[]{this.getClass().getName() + "#compare(Duration duration)"
 753                     + fieldType, field.toString()})
 754             );
 755         }
 756     }
 757 
 758     /**
 759      * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
 760      *
 761      * @param duration1  Unnormalized duration
 762      * @param duration2  Unnormalized duration
 763      * @return INDETERMINATE if the order relationship between date1 and date2 is indeterminate.
 764      * EQUAL if the order relation between date1 and date2 is EQUAL.
 765      * If the strict parameter is true, return LESS_THAN if date1 is less than date2 and
 766      * return GREATER_THAN if date1 is greater than date2.
 767      * If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and
 768      * return GREATER_THAN if date1 is greater than OR equal to date2
 769      */
 770     private int compareDates(Duration duration1, Duration duration2) {
 771 
 772         int resultA = DatatypeConstants.INDETERMINATE;
 773         int resultB = DatatypeConstants.INDETERMINATE;
 774 
 775         XMLGregorianCalendar tempA = (XMLGregorianCalendar)TEST_POINTS[0].clone();
 776         XMLGregorianCalendar tempB = (XMLGregorianCalendar)TEST_POINTS[0].clone();
 777 
 778         //long comparison algorithm is required
 779         tempA.add(duration1);
 780         tempB.add(duration2);
 781         resultA =  tempA.compare(tempB);
 782         if ( resultA == DatatypeConstants.INDETERMINATE ) {
 783             return DatatypeConstants.INDETERMINATE;
 784         }
 785 
 786         tempA = (XMLGregorianCalendar)TEST_POINTS[1].clone();
 787         tempB = (XMLGregorianCalendar)TEST_POINTS[1].clone();
 788 
 789         tempA.add(duration1);
 790         tempB.add(duration2);
 791         resultB = tempA.compare(tempB);
 792         resultA = compareResults(resultA, resultB);
 793         if (resultA == DatatypeConstants.INDETERMINATE) {
 794             return DatatypeConstants.INDETERMINATE;
 795         }
 796 
 797         tempA = (XMLGregorianCalendar)TEST_POINTS[2].clone();
 798         tempB = (XMLGregorianCalendar)TEST_POINTS[2].clone();
 799 
 800         tempA.add(duration1);
 801         tempB.add(duration2);
 802         resultB = tempA.compare(tempB);
 803         resultA = compareResults(resultA, resultB);
 804         if (resultA == DatatypeConstants.INDETERMINATE) {
 805             return DatatypeConstants.INDETERMINATE;
 806         }
 807 
 808         tempA = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 809         tempB = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 810 
 811         tempA.add(duration1);
 812         tempB.add(duration2);
 813         resultB = tempA.compare(tempB);
 814         resultA = compareResults(resultA, resultB);
 815 
 816         return resultA;
 817     }
 818 
 819     private int compareResults(int resultA, int resultB) {
 820 
 821         if ( resultB == DatatypeConstants.INDETERMINATE ) {
 822             return DatatypeConstants.INDETERMINATE;
 823         }
 824         else if ( resultA!=resultB) {
 825             return DatatypeConstants.INDETERMINATE;
 826         }
 827         return resultA;
 828     }
 829 
 830     /**
 831      * Returns a hash code consistent with the definition of the equals method.
 832      *
 833      * @see Object#hashCode()
 834      */
 835     public int hashCode() {
 836         // component wise hash is not correct because 1day = 24hours
 837         Calendar cal = TEST_POINTS[0].toGregorianCalendar();
 838         this.addTo(cal);
 839         return (int) getCalendarTimeInMillis(cal);
 840     }
 841 
 842     /**
 843      * Returns a string representation of this duration object.
 844      *
 845      * <p>
 846      * The result is formatter according to the XML Schema 1.0
 847      * spec and can be always parsed back later into the
 848      * equivalent duration object by
 849      * the {@link #DurationImpl(String)} constructor.
 850      *
 851      * <p>
 852      * Formally, the following holds for any {@link Duration}
 853      * object x.
 854      * <pre>
 855      * new Duration(x.toString()).equals(x)
 856      * </pre>
 857      *
 858      * @return
 859      *      Always return a non-null valid String object.
 860      */
 861     public String toString() {
 862         StringBuffer buf = new StringBuffer();
 863         if (signum < 0) {
 864             buf.append('-');
 865         }
 866         buf.append('P');
 867 
 868         if (years != null) {
 869             buf.append(years).append('Y');
 870         }
 871         if (months != null) {
 872             buf.append(months).append('M');
 873         }
 874         if (days != null) {
 875             buf.append(days).append('D');
 876         }
 877 
 878         if (hours != null || minutes != null || seconds != null) {
 879             buf.append('T');
 880             if (hours != null) {
 881                 buf.append(hours).append('H');
 882             }
 883             if (minutes != null) {
 884                 buf.append(minutes).append('M');
 885             }
 886             if (seconds != null) {
 887                 buf.append(toString(seconds)).append('S');
 888             }
 889         }
 890 
 891         return buf.toString();
 892     }
 893 
 894     /**
 895      * <p>Turns {@link BigDecimal} to a string representation.</p>
 896      *
 897      * <p>Due to a behavior change in the {@link BigDecimal#toString()}
 898      * method in JDK1.5, this had to be implemented here.</p>
 899      *
 900      * @param bd <code>BigDecimal</code> to format as a <code>String</code>
 901      *
 902      * @return  <code>String</code> representation of <code>BigDecimal</code>
 903      */
 904     private String toString(BigDecimal bd) {
 905         String intString = bd.unscaledValue().toString();
 906         int scale = bd.scale();
 907 
 908         if (scale == 0) {
 909             return intString;
 910         }
 911 
 912         /* Insert decimal point */
 913         StringBuffer buf;
 914         int insertionPoint = intString.length() - scale;
 915         if (insertionPoint == 0) { /* Point goes right before intVal */
 916             return "0." + intString;
 917         }
 918         else if (insertionPoint > 0) { /* Point goes inside intVal */
 919             buf = new StringBuffer(intString);
 920             buf.insert(insertionPoint, '.');
 921         }
 922         else { /* We must insert zeros between point and intVal */
 923             buf = new StringBuffer(3 - insertionPoint + intString.length());
 924             buf.append("0.");
 925             for (int i = 0; i < -insertionPoint; i++) {
 926                 buf.append('0');
 927             }
 928             buf.append(intString);
 929         }
 930         return buf.toString();
 931     }
 932 
 933     /**
 934      * Checks if a field is set.
 935      *
 936      * A field of a duration object may or may not be present.
 937      * This method can be used to test if a field is present.
 938      *
 939      * @param field
 940      *      one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
 941      *      MINUTES, or SECONDS.)
 942      * @return
 943      *      true if the field is present. false if not.
 944      *
 945      * @throws NullPointerException
 946      *      If the field parameter is null.
 947      */
 948     public boolean isSet(DatatypeConstants.Field field) {
 949 
 950         if (field == null) {
 951             String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)" ;
 952                 throw new NullPointerException(
 953                 //"cannot be called with field == null"
 954                 DatatypeMessageFormatter.formatMessage(null, "FieldCannotBeNull", new Object[]{methodName})
 955                 );
 956         }
 957 
 958         if (field == DatatypeConstants.YEARS) {
 959                         return years != null;
 960         }
 961 
 962                 if (field == DatatypeConstants.MONTHS) {
 963                         return months != null;
 964                 }
 965 
 966                 if (field == DatatypeConstants.DAYS) {
 967                         return days != null;
 968                 }
 969 
 970                 if (field == DatatypeConstants.HOURS) {
 971                         return hours != null;
 972                 }
 973 
 974                 if (field == DatatypeConstants.MINUTES) {
 975                         return minutes != null;
 976                 }
 977 
 978                 if (field == DatatypeConstants.SECONDS) {
 979                         return seconds != null;
 980                 }
 981         String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)";
 982 
 983         throw new IllegalArgumentException(
 984             DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object[]{methodName, field.toString()})
 985                 );
 986 
 987     }
 988 
 989     /**
 990      * Gets the value of a field.
 991      *
 992      * Fields of a duration object may contain arbitrary large value.
 993      * Therefore this method is designed to return a {@link Number} object.
 994      *
 995      * In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned
 996      * number will be a non-negative integer. In case of seconds,
 997      * the returned number may be a non-negative decimal value.
 998      *
 999      * @param field
1000      *      one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1001      *      MINUTES, or SECONDS.)
1002      * @return
1003      *      If the specified field is present, this method returns
1004      *      a non-null non-negative {@link Number} object that
1005      *      represents its value. If it is not present, return null.
1006      *      For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method
1007      *      returns a {@link BigInteger} object. For SECONDS, this
1008      *      method returns a {@link BigDecimal}.
1009      *
1010      * @throws NullPointerException
1011      *      If the field parameter is null.
1012      */
1013     public Number getField(DatatypeConstants.Field field) {
1014 
1015                 if (field == null) {
1016             String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field) " ;
1017 
1018                         throw new NullPointerException(
1019                 DatatypeMessageFormatter.formatMessage(null,"FieldCannotBeNull", new Object[]{methodName})
1020                 );
1021                 }
1022 
1023                 if (field == DatatypeConstants.YEARS) {
1024                         return years;
1025                 }
1026 
1027                 if (field == DatatypeConstants.MONTHS) {
1028                         return months;
1029                 }
1030 
1031                 if (field == DatatypeConstants.DAYS) {
1032                         return days;
1033                 }
1034 
1035                 if (field == DatatypeConstants.HOURS) {
1036                         return hours;
1037                 }
1038 
1039                 if (field == DatatypeConstants.MINUTES) {
1040                         return minutes;
1041                 }
1042 
1043                 if (field == DatatypeConstants.SECONDS) {
1044                         return seconds;
1045                 }
1046                 /**
1047                 throw new IllegalArgumentException(
1048                         "javax.xml.datatype.Duration"
1049                         + "#(getSet(DatatypeConstants.Field field) called with an unknown field: "
1050                         + field.toString()
1051                 );
1052         */
1053         String methodName = "javax.xml.datatype.Duration" + "#(getSet(DatatypeConstants.Field field)";
1054 
1055         throw new IllegalArgumentException(
1056             DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object[]{methodName, field.toString()})
1057                 );
1058 
1059     }
1060 
1061     /**
1062      * Obtains the value of the YEARS field as an integer value,
1063      * or 0 if not present.
1064      *
1065      * <p>
1066      * This method is a convenience method around the
1067      * {@link #getField(DatatypeConstants.Field)} method.
1068      *
1069      * <p>
1070      * Note that since this method returns <tt>int</tt>, this
1071      * method will return an incorrect value for {@link Duration}s
1072      * with the year field that goes beyond the range of <tt>int</tt>.
1073      * Use <code>getField(YEARS)</code> to avoid possible loss of precision.</p>
1074      *
1075      * @return
1076      *      If the YEARS field is present, return
1077      *      its value as an integer by using the {@link Number#intValue()}
1078      *      method. If the YEARS field is not present, return 0.
1079      */
1080     public int getYears() {
1081         return getInt(DatatypeConstants.YEARS);
1082     }
1083 
1084     /**
1085      * Obtains the value of the MONTHS field as an integer value,
1086      * or 0 if not present.
1087      *
1088      * This method works just like {@link #getYears()} except
1089      * that this method works on the MONTHS field.
1090      *
1091      * @return Months of this <code>Duration</code>.
1092      */
1093     public int getMonths() {
1094         return getInt(DatatypeConstants.MONTHS);
1095     }
1096 
1097     /**
1098      * Obtains the value of the DAYS field as an integer value,
1099      * or 0 if not present.
1100      *
1101      * This method works just like {@link #getYears()} except
1102      * that this method works on the DAYS field.
1103      *
1104      * @return Days of this <code>Duration</code>.
1105      */
1106     public int getDays() {
1107         return getInt(DatatypeConstants.DAYS);
1108     }
1109 
1110     /**
1111      * Obtains the value of the HOURS field as an integer value,
1112      * or 0 if not present.
1113      *
1114      * This method works just like {@link #getYears()} except
1115      * that this method works on the HOURS field.
1116      *
1117      * @return Hours of this <code>Duration</code>.
1118      *
1119      */
1120     public int getHours() {
1121         return getInt(DatatypeConstants.HOURS);
1122     }
1123 
1124     /**
1125      * Obtains the value of the MINUTES field as an integer value,
1126      * or 0 if not present.
1127      *
1128      * This method works just like {@link #getYears()} except
1129      * that this method works on the MINUTES field.
1130      *
1131      * @return Minutes of this <code>Duration</code>.
1132      *
1133      */
1134     public int getMinutes() {
1135         return getInt(DatatypeConstants.MINUTES);
1136     }
1137 
1138     /**
1139      * Obtains the value of the SECONDS field as an integer value,
1140      * or 0 if not present.
1141      *
1142      * This method works just like {@link #getYears()} except
1143      * that this method works on the SECONDS field.
1144      *
1145      * @return seconds in the integer value. The fraction of seconds
1146      *   will be discarded (for example, if the actual value is 2.5,
1147      *   this method returns 2)
1148      */
1149     public int getSeconds() {
1150         return getInt(DatatypeConstants.SECONDS);
1151     }
1152 
1153     /**
1154      * <p>Return the requested field value as an int.</p>
1155      *
1156      * <p>If field is not set, i.e. == null, 0 is returned.</p>
1157      *
1158      * @param field To get value for.
1159      *
1160      * @return int value of field or 0 if field is not set.
1161      */
1162     private int getInt(DatatypeConstants.Field field) {
1163         Number n = getField(field);
1164         if (n == null) {
1165             return 0;
1166         }
1167         else {
1168             return n.intValue();
1169         }
1170     }
1171 
1172     /**
1173      * <p>Returns the length of the duration in milli-seconds.</p>
1174      *
1175      * <p>If the seconds field carries more digits than milli-second order,
1176      * those will be simply discarded (or in other words, rounded to zero.)
1177      * For example, for any Calendar value <code>x<code>,</p>
1178      * <pre>
1179      * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1180      * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1181      * </pre>
1182      *
1183      * <p>
1184      * Note that this method uses the {@link #addTo(Calendar)} method,
1185      * which may work incorectly with {@link Duration} objects with
1186      * very large values in its fields. See the {@link #addTo(Calendar)}
1187      * method for details.
1188      *
1189      * @param startInstant
1190      *      The length of a month/year varies. The <code>startInstant</code> is
1191      *      used to disambiguate this variance. Specifically, this method
1192      *      returns the difference between <code>startInstant</code> and
1193      *      <code>startInstant+duration</code>
1194      *
1195      * @return milliseconds between <code>startInstant</code> and
1196      *   <code>startInstant</code> plus this <code>Duration</code>
1197      *
1198      * @throws NullPointerException if <code>startInstant</code> parameter
1199      * is null.
1200      *
1201      */
1202     public long getTimeInMillis(final Calendar startInstant) {
1203         Calendar cal = (Calendar) startInstant.clone();
1204         addTo(cal);
1205         return getCalendarTimeInMillis(cal) - getCalendarTimeInMillis(startInstant);
1206     }
1207 
1208     /**
1209      * <p>Returns the length of the duration in milli-seconds.</p>
1210      *
1211      * <p>If the seconds field carries more digits than milli-second order,
1212      * those will be simply discarded (or in other words, rounded to zero.)
1213      * For example, for any <code>Date</code> value <code>x<code>,</p>
1214      * <pre>
1215      * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1216      * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1217      * </pre>
1218      *
1219      * <p>
1220      * Note that this method uses the {@link #addTo(Date)} method,
1221      * which may work incorectly with {@link Duration} objects with
1222      * very large values in its fields. See the {@link #addTo(Date)}
1223      * method for details.
1224      *
1225      * @param startInstant
1226      *      The length of a month/year varies. The <code>startInstant</code> is
1227      *      used to disambiguate this variance. Specifically, this method
1228      *      returns the difference between <code>startInstant</code> and
1229      *      <code>startInstant+duration</code>.
1230      *
1231      * @throws NullPointerException
1232      *      If the startInstant parameter is null.
1233      *
1234      * @return milliseconds between <code>startInstant</code> and
1235      *   <code>startInstant</code> plus this <code>Duration</code>
1236      *
1237      * @see #getTimeInMillis(Calendar)
1238      */
1239     public long getTimeInMillis(final Date startInstant) {
1240         Calendar cal = new GregorianCalendar();
1241         cal.setTime(startInstant);
1242         this.addTo(cal);
1243         return getCalendarTimeInMillis(cal) - startInstant.getTime();
1244     }
1245 
1246 //    /**
1247 //     * Returns an equivalent but "normalized" duration value.
1248 //     *
1249 //     * Intuitively, the normalization moves YEARS into
1250 //     * MONTHS (by x12) and moves DAYS, HOURS, and MINUTES fields
1251 //     * into SECONDS (by x86400, x3600, and x60 respectively.)
1252 //     *
1253 //     *
1254 //     * Formally, this method satisfies the following conditions:
1255 //     * <ul>
1256 //     *  <li>x.normalize().equals(x)
1257 //     *  <li>!x.normalize().isSet(Duration.YEARS)
1258 //     *  <li>!x.normalize().isSet(Duration.DAYS)
1259 //     *  <li>!x.normalize().isSet(Duration.HOURS)
1260 //     *  <li>!x.normalize().isSet(Duration.MINUTES)
1261 //     * </ul>
1262 //     *
1263 //     * @return
1264 //     *      always return a non-null valid value.
1265 //     */
1266 //    public Duration normalize() {
1267 //        return null;
1268 //    }
1269 
1270     /**
1271      * <p>Converts the years and months fields into the days field
1272      * by using a specific time instant as the reference point.</p>
1273      *
1274      * <p>For example, duration of one month normalizes to 31 days
1275      * given the start time instance "July 8th 2003, 17:40:32".</p>
1276      *
1277      * <p>Formally, the computation is done as follows:</p>
1278      * <ol>
1279      *  <li>The given Calendar object is cloned.
1280      *  <li>The years, months and days fields will be added to
1281      *      the {@link Calendar} object
1282      *      by using the {@link Calendar#add(int,int)} method.
1283      *  <li>The difference between two Calendars are computed in terms of days.
1284      *  <li>The computed days, along with the hours, minutes and seconds
1285      *      fields of this duration object is used to construct a new
1286      *      Duration object.
1287      * </ol>
1288      *
1289      * <p>Note that since the Calendar class uses <code>int</code> to
1290      * hold the value of year and month, this method may produce
1291      * an unexpected result if this duration object holds
1292      * a very large value in the years or months fields.</p>
1293      *
1294      * @param startTimeInstant <code>Calendar</code> reference point.
1295      *
1296      * @return <code>Duration</code> of years and months of this <code>Duration</code> as days.
1297      *
1298      * @throws NullPointerException If the startTimeInstant parameter is null.
1299      */
1300     public Duration normalizeWith(Calendar startTimeInstant) {
1301 
1302         Calendar c = (Calendar) startTimeInstant.clone();
1303 
1304         // using int may cause overflow, but
1305         // Calendar internally treats value as int anyways.
1306         c.add(Calendar.YEAR, getYears() * signum);
1307         c.add(Calendar.MONTH, getMonths() * signum);
1308         c.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1309 
1310         // obtain the difference in terms of days
1311         long diff = getCalendarTimeInMillis(c) - getCalendarTimeInMillis(startTimeInstant);
1312         int days = (int) (diff / (1000L * 60L * 60L * 24L));
1313 
1314         return new DurationImpl(
1315                 days >= 0,
1316                 null,
1317                 null,
1318                 wrap(Math.abs(days)),
1319                 (BigInteger) getField(DatatypeConstants.HOURS),
1320                 (BigInteger) getField(DatatypeConstants.MINUTES),
1321                 (BigDecimal) getField(DatatypeConstants.SECONDS));
1322     }
1323 
1324     /**
1325      * <p>Computes a new duration whose value is <code>factor</code> times
1326      * longer than the value of this duration.</p>
1327      *
1328      * <p>This method is provided for the convenience.
1329      * It is functionally equivalent to the following code:</p>
1330      * <pre>
1331      * multiply(new BigDecimal(String.valueOf(factor)))
1332      * </pre>
1333      *
1334      * @param factor Factor times longer of new <code>Duration</code> to create.
1335      *
1336      * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>.
1337      *
1338      * @see #multiply(BigDecimal)
1339      */
1340     public Duration multiply(int factor) {
1341         return multiply(BigDecimal.valueOf(factor));
1342     }
1343 
1344     /**
1345      * Computes a new duration whose value is <code>factor</code> times
1346      * longer than the value of this duration.
1347      *
1348      * <p>
1349      * For example,
1350      * <pre>
1351      * "P1M" (1 month) * "12" = "P12M" (12 months)
1352      * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
1353      * "P1M" (1 month) * "1.5" = IllegalStateException
1354      * </pre>
1355      *
1356      * <p>
1357      * Since the {@link Duration} class is immutable, this method
1358      * doesn't change the value of this object. It simply computes
1359      * a new Duration object and returns it.
1360      *
1361      * <p>
1362      * The operation will be performed field by field with the precision
1363      * of {@link BigDecimal}. Since all the fields except seconds are
1364      * restricted to hold integers,
1365      * any fraction produced by the computation will be
1366      * carried down toward the next lower unit. For example,
1367      * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,
1368      * which will be carried down to "PT12H" (12 hours).
1369      * When fractions of month cannot be meaningfully carried down
1370      * to days, or year to months, this will cause an
1371      * {@link IllegalStateException} to be thrown.
1372      * For example if you multiple one month by 0.5.</p>
1373      *
1374      * <p>
1375      * To avoid {@link IllegalStateException}, use
1376      * the {@link #normalizeWith(Calendar)} method to remove the years
1377      * and months fields.
1378      *
1379      * @param factor to multiply by
1380      *
1381      * @return
1382      *      returns a non-null valid {@link Duration} object
1383      *
1384      * @throws IllegalStateException if operation produces fraction in
1385      * the months field.
1386      *
1387      * @throws NullPointerException if the <code>factor</code> parameter is
1388      * <code>null</code>.
1389      *
1390      */
1391     public Duration multiply(BigDecimal factor) {
1392         BigDecimal carry = ZERO;
1393         int factorSign = factor.signum();
1394         factor = factor.abs();
1395 
1396         BigDecimal[] buf = new BigDecimal[6];
1397 
1398         for (int i = 0; i < 5; i++) {
1399             BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);
1400             bd = bd.multiply(factor).add(carry);
1401 
1402             buf[i] = bd.setScale(0, RoundingMode.DOWN);
1403 
1404             bd = bd.subtract(buf[i]);
1405             if (i == 1) {
1406                 if (bd.signum() != 0) {
1407                     throw new IllegalStateException(); // illegal carry-down
1408                 } else {
1409                     carry = ZERO;
1410                 }
1411             }
1412             else {
1413                 carry = bd.multiply(FACTORS[i]);
1414             }
1415         }
1416 
1417         if (seconds != null) {
1418             buf[5] = seconds.multiply(factor).add(carry);
1419         }
1420         else {
1421             buf[5] = carry;
1422         }
1423 
1424         return new DurationImpl(
1425             this.signum * factorSign >= 0,
1426             toBigInteger(buf[0], null == years),
1427             toBigInteger(buf[1], null == months),
1428             toBigInteger(buf[2], null == days),
1429             toBigInteger(buf[3], null == hours),
1430             toBigInteger(buf[4], null == minutes),
1431             (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
1432     }
1433 
1434     /**
1435      * <p>Gets the value of the field as a {@link BigDecimal}.</p>
1436      *
1437      * <p>If the field is unset, return 0.</p>
1438      *
1439      * @param f Field to get value for.
1440      *
1441      * @return  non-null valid {@link BigDecimal}.
1442      */
1443     private BigDecimal getFieldAsBigDecimal(DatatypeConstants.Field f) {
1444         if (f == DatatypeConstants.SECONDS) {
1445             if (seconds != null) {
1446                 return seconds;
1447             }
1448             else {
1449                 return ZERO;
1450             }
1451         }
1452         else {
1453             BigInteger bi = (BigInteger) getField(f);
1454             if (bi == null) {
1455                 return ZERO;
1456             }
1457             else {
1458                 return new BigDecimal(bi);
1459             }
1460         }
1461     }
1462 
1463     /**
1464      * <p>BigInteger value of BigDecimal value.</p>
1465      *
1466      * @param value Value to convert.
1467      * @param canBeNull Can returned value be null?
1468      *
1469      * @return BigInteger value of BigDecimal, possibly null.
1470      */
1471     private static BigInteger toBigInteger(
1472         BigDecimal value,
1473         boolean canBeNull) {
1474         if (canBeNull && value.signum() == 0) {
1475             return null;
1476         }
1477         else {
1478             return value.unscaledValue();
1479         }
1480     }
1481 
1482     /**
1483      * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
1484      * FIELDS[i+1].
1485      */
1486     private static final BigDecimal[] FACTORS = new BigDecimal[] {
1487         BigDecimal.valueOf(12),
1488         null/*undefined*/,
1489         BigDecimal.valueOf(24),
1490         BigDecimal.valueOf(60),
1491         BigDecimal.valueOf(60)
1492     };
1493 
1494     /**
1495      * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
1496      *
1497      * <p>For example,</p>
1498      * <pre>
1499      * "1 day" + "-3 days" = "-2 days"
1500      * "1 year" + "1 day" = "1 year and 1 day"
1501      * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
1502      * "15 hours" + "-3 days" = "-(2 days,9 hours)"
1503      * "1 year" + "-1 day" = IllegalStateException
1504      * </pre>
1505      *
1506      * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1507      * there are cases where the operation fails in
1508      * {@link IllegalStateException}.</p>
1509      *
1510      * <p>
1511      * Formally, the computation is defined as follows.</p>
1512      * <p>
1513      * Firstly, we can assume that two {@link Duration}s to be added
1514      * are both positive without losing generality (i.e.,
1515      * <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>,
1516      * <code>(-X)+(-Y)=-(X+Y)</code>)
1517      *
1518      * <p>
1519      * Addition of two positive {@link Duration}s are simply defined as
1520      * field by field addition where missing fields are treated as 0.
1521      * <p>
1522      * A field of the resulting {@link Duration} will be unset if and
1523      * only if respective fields of two input {@link Duration}s are unset.
1524      * <p>
1525      * Note that <code>lhs.add(rhs)</code> will be always successful if
1526      * <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are
1527      * normalized.</p>
1528      *
1529      * @param rhs <code>Duration</code> to add to this <code>Duration</code>
1530      *
1531      * @return
1532      *      non-null valid Duration object.
1533      *
1534      * @throws NullPointerException
1535      *      If the rhs parameter is null.
1536      * @throws IllegalStateException
1537      *      If two durations cannot be meaningfully added. For
1538      *      example, adding negative one day to one month causes
1539      *      this exception.
1540      *
1541      *
1542      * @see #subtract(Duration)
1543      */
1544     public Duration add(final Duration rhs) {
1545         Duration lhs = this;
1546         BigDecimal[] buf = new BigDecimal[6];
1547 
1548         buf[0] = sanitize((BigInteger) lhs.getField(DatatypeConstants.YEARS),
1549                 lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.YEARS),  rhs.getSign()));
1550         buf[1] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MONTHS),
1551                 lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MONTHS), rhs.getSign()));
1552         buf[2] = sanitize((BigInteger) lhs.getField(DatatypeConstants.DAYS),
1553                 lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.DAYS),   rhs.getSign()));
1554         buf[3] = sanitize((BigInteger) lhs.getField(DatatypeConstants.HOURS),
1555                 lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.HOURS),  rhs.getSign()));
1556         buf[4] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MINUTES),
1557                 lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MINUTES), rhs.getSign()));
1558         buf[5] = sanitize((BigDecimal) lhs.getField(DatatypeConstants.SECONDS),
1559                 lhs.getSign()).add(sanitize((BigDecimal) rhs.getField(DatatypeConstants.SECONDS), rhs.getSign()));
1560 
1561         // align sign
1562         alignSigns(buf, 0, 2); // Y,M
1563         alignSigns(buf, 2, 6); // D,h,m,s
1564 
1565         // make sure that the sign bit is consistent across all 6 fields.
1566         int s = 0;
1567         for (int i = 0; i < 6; i++) {
1568             if (s * buf[i].signum() < 0) {
1569                 throw new IllegalStateException();
1570             }
1571             if (s == 0) {
1572                 s = buf[i].signum();
1573             }
1574         }
1575 
1576         return new DurationImpl(
1577             s >= 0,
1578             toBigInteger(sanitize(buf[0], s),
1579                 lhs.getField(DatatypeConstants.YEARS) == null && rhs.getField(DatatypeConstants.YEARS) == null),
1580             toBigInteger(sanitize(buf[1], s),
1581                 lhs.getField(DatatypeConstants.MONTHS) == null && rhs.getField(DatatypeConstants.MONTHS) == null),
1582             toBigInteger(sanitize(buf[2], s),
1583                 lhs.getField(DatatypeConstants.DAYS) == null && rhs.getField(DatatypeConstants.DAYS) == null),
1584             toBigInteger(sanitize(buf[3], s),
1585                 lhs.getField(DatatypeConstants.HOURS) == null && rhs.getField(DatatypeConstants.HOURS) == null),
1586             toBigInteger(sanitize(buf[4], s),
1587                 lhs.getField(DatatypeConstants.MINUTES) == null && rhs.getField(DatatypeConstants.MINUTES) == null),
1588              (buf[5].signum() == 0
1589              && lhs.getField(DatatypeConstants.SECONDS) == null
1590              && rhs.getField(DatatypeConstants.SECONDS) == null) ? null : sanitize(buf[5], s));
1591     }
1592 
1593     private static void alignSigns(BigDecimal[] buf, int start, int end) {
1594         // align sign
1595         boolean touched;
1596 
1597         do { // repeat until all the sign bits become consistent
1598             touched = false;
1599             int s = 0; // sign of the left fields
1600 
1601             for (int i = start; i < end; i++) {
1602                 if (s * buf[i].signum() < 0) {
1603                     // this field has different sign than its left field.
1604                     touched = true;
1605 
1606                     // compute the number of unit that needs to be borrowed.
1607                     BigDecimal borrow =
1608                         buf[i].abs().divide(
1609                             FACTORS[i - 1],
1610                             RoundingMode.UP);
1611                     if (buf[i].signum() > 0) {
1612                         borrow = borrow.negate();
1613                     }
1614 
1615                     // update values
1616                     buf[i - 1] = buf[i - 1].subtract(borrow);
1617                     buf[i] = buf[i].add(borrow.multiply(FACTORS[i - 1]));
1618                 }
1619                 if (buf[i].signum() != 0) {
1620                     s = buf[i].signum();
1621                 }
1622             }
1623         } while (touched);
1624     }
1625 
1626     /**
1627      * Compute <code>value*signum</code> where value==null is treated as
1628      * value==0.
1629      * @param value Value to sanitize.
1630      * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1631      *
1632      * @return non-null {@link BigDecimal}.
1633      */
1634     private static BigDecimal sanitize(BigInteger value, int signum) {
1635         if (signum == 0 || value == null) {
1636             return ZERO;
1637         }
1638         if (signum > 0) {
1639             return new BigDecimal(value);
1640         }
1641         return new BigDecimal(value.negate());
1642     }
1643 
1644     /**
1645      * <p>Compute <code>value*signum</code> where <code>value==null</code> is treated as <code>value==0</code></p>.
1646      *
1647      * @param value Value to sanitize.
1648      * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
1649      *
1650      * @return non-null {@link BigDecimal}.
1651      */
1652     static BigDecimal sanitize(BigDecimal value, int signum) {
1653         if (signum == 0 || value == null) {
1654             return ZERO;
1655         }
1656         if (signum > 0) {
1657             return value;
1658         }
1659         return value.negate();
1660     }
1661 
1662     /**
1663      * <p>Computes a new duration whose value is <code>this-rhs</code>.</p>
1664      *
1665      * <p>For example:</p>
1666      * <pre>
1667      * "1 day" - "-3 days" = "4 days"
1668      * "1 year" - "1 day" = IllegalStateException
1669      * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)"
1670      * "15 hours" - "-3 days" = "3 days and 15 hours"
1671      * "1 year" - "-1 day" = "1 year and 1 day"
1672      * </pre>
1673      *
1674      * <p>Since there's no way to meaningfully subtract 1 day from 1 month,
1675      * there are cases where the operation fails in {@link IllegalStateException}.</p>
1676      *
1677      * <p>Formally the computation is defined as follows.
1678      * First, we can assume that two {@link Duration}s are both positive
1679      * without losing generality.  (i.e.,
1680      * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>,
1681      * <code>(-X)-(-Y)=-(X-Y)</code>)</p>
1682      *
1683      * <p>Then two durations are subtracted field by field.
1684      * If the sign of any non-zero field <tt>F</tt> is different from
1685      * the sign of the most significant field,
1686      * 1 (if <tt>F</tt> is negative) or -1 (otherwise)
1687      * will be borrowed from the next bigger unit of <tt>F</tt>.</p>
1688      *
1689      * <p>This process is repeated until all the non-zero fields have
1690      * the same sign.</p>
1691      *
1692      * <p>If a borrow occurs in the days field (in other words, if
1693      * the computation needs to borrow 1 or -1 month to compensate
1694      * days), then the computation fails by throwing an
1695      * {@link IllegalStateException}.</p>
1696      *
1697      * @param rhs <code>Duration</code> to substract from this <code>Duration</code>.
1698      *
1699      * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>.
1700      *
1701      * @throws IllegalStateException
1702      *      If two durations cannot be meaningfully subtracted. For
1703      *      example, subtracting one day from one month causes
1704      *      this exception.
1705      *
1706      * @throws NullPointerException
1707      *      If the rhs parameter is null.
1708      *
1709      * @see #add(Duration)
1710      */
1711     public Duration subtract(final Duration rhs) {
1712         return add(rhs.negate());
1713     }
1714 
1715     /**
1716      * Returns a new {@link Duration} object whose
1717      * value is <code>-this</code>.
1718      *
1719      * <p>
1720      * Since the {@link Duration} class is immutable, this method
1721      * doesn't change the value of this object. It simply computes
1722      * a new Duration object and returns it.
1723      *
1724      * @return
1725      *      always return a non-null valid {@link Duration} object.
1726      */
1727     public Duration negate() {
1728         return new DurationImpl(
1729             signum <= 0,
1730             years,
1731             months,
1732             days,
1733             hours,
1734             minutes,
1735             seconds);
1736     }
1737 
1738     /**
1739      * Returns the sign of this duration in -1,0, or 1.
1740      *
1741      * @return
1742      *      -1 if this duration is negative, 0 if the duration is zero,
1743      *      and 1 if the duration is postive.
1744      */
1745     public int signum() {
1746         return signum;
1747     }
1748 
1749 
1750     /**
1751      * Adds this duration to a {@link Calendar} object.
1752      *
1753      * <p>
1754      * Calls {@link java.util.Calendar#add(int,int)} in the
1755      * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS
1756      * if those fields are present. Because the {@link Calendar} class
1757      * uses int to hold values, there are cases where this method
1758      * won't work correctly (for example if values of fields
1759      * exceed the range of int.)
1760      * </p>
1761      *
1762      * <p>
1763      * Also, since this duration class is a Gregorian duration, this
1764      * method will not work correctly if the given {@link Calendar}
1765      * object is based on some other calendar systems.
1766      * </p>
1767      *
1768      * <p>
1769      * Any fractional parts of this {@link Duration} object
1770      * beyond milliseconds will be simply ignored. For example, if
1771      * this duration is "P1.23456S", then 1 is added to SECONDS,
1772      * 234 is added to MILLISECONDS, and the rest will be unused.
1773      * </p>
1774      *
1775      * <p>
1776      * Note that because {@link Calendar#add(int, int)} is using
1777      * <tt>int</tt>, {@link Duration} with values beyond the
1778      * range of <tt>int</tt> in its fields
1779      * will cause overflow/underflow to the given {@link Calendar}.
1780      * {@link XMLGregorianCalendar#add(Duration)} provides the same
1781      * basic operation as this method while avoiding
1782      * the overflow/underflow issues.
1783      *
1784      * @param calendar
1785      *      A calendar object whose value will be modified.
1786      * @throws NullPointerException
1787      *      if the calendar parameter is null.
1788      */
1789     public void addTo(Calendar calendar) {
1790         calendar.add(Calendar.YEAR, getYears() * signum);
1791         calendar.add(Calendar.MONTH, getMonths() * signum);
1792         calendar.add(Calendar.DAY_OF_MONTH, getDays() * signum);
1793         calendar.add(Calendar.HOUR, getHours() * signum);
1794         calendar.add(Calendar.MINUTE, getMinutes() * signum);
1795         calendar.add(Calendar.SECOND, getSeconds() * signum);
1796 
1797         if (seconds != null) {
1798             BigDecimal fraction =
1799                 seconds.subtract(seconds.setScale(0, RoundingMode.DOWN));
1800             int millisec = fraction.movePointRight(3).intValue();
1801             calendar.add(Calendar.MILLISECOND, millisec * signum);
1802         }
1803     }
1804 
1805     /**
1806      * Adds this duration to a {@link Date} object.
1807      *
1808      * <p>
1809      * The given date is first converted into
1810      * a {@link java.util.GregorianCalendar}, then the duration
1811      * is added exactly like the {@link #addTo(Calendar)} method.
1812      *
1813      * <p>
1814      * The updated time instant is then converted back into a
1815      * {@link Date} object and used to update the given {@link Date} object.
1816      *
1817      * <p>
1818      * This somewhat redundant computation is necessary to unambiguously
1819      * determine the duration of months and years.
1820      *
1821      * @param date
1822      *      A date object whose value will be modified.
1823      * @throws NullPointerException
1824      *      if the date parameter is null.
1825      */
1826     public void addTo(Date date) {
1827         Calendar cal = new GregorianCalendar();
1828         cal.setTime(date); // this will throw NPE if date==null
1829         this.addTo(cal);
1830         date.setTime(getCalendarTimeInMillis(cal));
1831     }
1832 
1833     /**
1834      * Returns time value in milliseconds
1835      * @param cal A calendar object
1836      * @return time value
1837      *
1838      * Diff from Xerces; Use JDK 1.5 feature.
1839      */
1840     private static long getCalendarTimeInMillis(Calendar cal) {
1841         return cal.getTimeInMillis();
1842     }
1843 
1844     /**
1845      * <p>Stream Unique Identifier.</p>
1846      *
1847      * <p>Serialization uses the lexical form returned by toString().</p>
1848      */
1849     private static final long serialVersionUID = 1L;
1850 
1851     /**
1852      * Writes {@link Duration} as a lexical representation
1853      * for maximum future compatibility.
1854      *
1855      * @return
1856      *      An object that encapsulates the string
1857      *      returned by <code>this.toString()</code>.
1858      */
1859     private Object writeReplace() throws IOException {
1860         return new DurationStream(this.toString());
1861     }
1862 
1863     /**
1864      * Representation of {@link Duration} in the object stream.
1865      *
1866      * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
1867      */
1868     private static class DurationStream implements Serializable {
1869         private final String lexical;
1870 
1871         private DurationStream(String _lexical) {
1872             this.lexical = _lexical;
1873         }
1874 
1875         private Object readResolve() throws ObjectStreamException {
1876             return new DurationImpl(lexical);
1877         }
1878 
1879         private static final long serialVersionUID = 1L;
1880     }
1881 
1882 }