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