jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java

Print this page


   1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2005 The Apache Software Foundation.





   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * 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 java.io.IOException;
  24 import java.io.ObjectStreamException;
  25 import java.io.Serializable;
  26 import java.math.BigDecimal;
  27 import java.math.BigInteger;
  28 import java.util.Calendar;
  29 import java.util.Date;
  30 import java.util.GregorianCalendar;
  31 import java.util.TimeZone;


  89  *
  90  *
  91  * <h2>Range of allowed values</h2>
  92  * <p>
  93  * Because some operations of {@link Duration} rely on {@link Calendar}
  94  * even though {@link Duration} can hold very large or very small values,
  95  * some of the methods may not work correctly on such {@link Duration}s.
  96  * The impacted methods document their dependency on {@link Calendar}.
  97  *
  98  *
  99  * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
 100  * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
 101  * @version $Revision: 1.8 $, $Date: 2010/05/19 23:20:06 $
 102 
 103  * @see XMLGregorianCalendar#add(Duration)
 104  */
 105 class DurationImpl
 106         extends Duration
 107         implements Serializable {
 108 
 109     /**
 110      * <p>Number of Fields.</p>
 111      */
 112     private static final int FIELD_NUM = 6;
 113 
 114     /**
 115      * <p>Internal array of value Fields.</p>
 116      */
 117         private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
 118                         DatatypeConstants.YEARS,
 119                         DatatypeConstants.MONTHS,
 120                         DatatypeConstants.DAYS,
 121                         DatatypeConstants.HOURS,
 122                         DatatypeConstants.MINUTES,
 123                         DatatypeConstants.SECONDS
 124                 };
 125 
 126                 /**
 127                  * <p>Internal array of value Field ids.</p>
 128                  */
 129                 private static final int[] FIELD_IDS = {
 130                                 DatatypeConstants.YEARS.getId(),
 131                                 DatatypeConstants.MONTHS.getId(),
 132                                 DatatypeConstants.DAYS.getId(),
 133                                 DatatypeConstants.HOURS.getId(),
 134                                 DatatypeConstants.MINUTES.getId(),
 135                                 DatatypeConstants.SECONDS.getId()
 136                         };
 137 
 138     /**
 139      * TimeZone for GMT.
 140      */
 141     private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
 142 
 143         /**
 144          * <p>BigDecimal value of 0.</p>
 145          */
 146         private static final BigDecimal ZERO = BigDecimal.valueOf((long) 0);
 147 
 148     /**
 149      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
 150      * zero, or positive.</p>
 151      */
 152     protected int signum;
 153 
 154     /**
 155      * <p>Years of this <code>Duration</code>.</p>
 156      */
 157     /**
 158      * These were final since Duration is immutable. But new subclasses need
 159      * to be able to set after conversion. It won't break the immutable nature
 160      * of them since there's no other way to set new values to them
 161      */
 162     protected BigInteger years;
 163 
 164     /**
 165      * <p>Months of this <code>Duration</code>.</p>
 166      */


 199         }
 200 
 201         /**
 202          * TODO: Javadoc
 203          * @param isPositive Sign.
 204          *
 205          * @return 1 if positive, else -1.
 206          */
 207     protected int calcSignum(boolean isPositive) {
 208         if ((years == null || years.signum() == 0)
 209             && (months == null || months.signum() == 0)
 210             && (days == null || days.signum() == 0)
 211             && (hours == null || hours.signum() == 0)
 212             && (minutes == null || minutes.signum() == 0)
 213             && (seconds == null || seconds.signum() == 0)) {
 214             return 0;
 215             }
 216 
 217             if (isPositive) {
 218                 return 1;
 219             } else {

 220                 return -1;
 221             }
 222 
 223     }
 224 
 225     /**
 226      * <p>Constructs a new Duration object by specifying each field individually.</p>
 227      *
 228      * <p>All the parameters are optional as long as at least one field is present.
 229      * If specified, parameters have to be zero or positive.</p>
 230      *
 231      * @param isPositive Set to <code>false</code> to create a negative duration. When the length
 232      *   of the duration is zero, this parameter will be ignored.
 233      * @param years of this <code>Duration</code>
 234      * @param months of this <code>Duration</code>
 235      * @param days of this <code>Duration</code>
 236      * @param hours of this <code>Duration</code>
 237      * @param minutes of this <code>Duration</code>
 238      * @param seconds of this <code>Duration</code>
 239      *
 240      * @throws IllegalArgumentException
 241      *    If years, months, days, hours, minutes and
 242      *    seconds parameters are all <code>null</code>. Or if any


 340             wrap(hours),
 341             wrap(minutes),
 342             seconds != DatatypeConstants.FIELD_UNDEFINED ? new BigDecimal(String.valueOf(seconds)) : null);
 343     }
 344 
 345         /**
 346          * TODO: Javadoc
 347          *
 348          * @param i int to convert to BigInteger.
 349          *
 350          * @return BigInteger representation of int.
 351          */
 352     protected static BigInteger wrap(final int i) {
 353 
 354         // field may not be set
 355         if (i == DatatypeConstants.FIELD_UNDEFINED) {
 356                 return null;
 357         }
 358 
 359         // int -> BigInteger
 360         return new BigInteger(String.valueOf(i));
 361     }
 362 
 363     /**
 364      * <p>Constructs a new Duration object by specifying the duration
 365      * in milliseconds.</p>
 366      *
 367      * @param durationInMilliSeconds
 368      *      The length of the duration in milliseconds.
 369      */
 370     protected DurationImpl(final long durationInMilliSeconds) {
 371 
 372         long l = durationInMilliSeconds;
 373 
 374         if (l > 0) {
 375             signum = 1;
 376         } else if (l < 0) {

 377             signum = -1;
 378             if (l == 0x8000000000000000L) {
 379                 // negating 0x8000000000000000L causes an overflow
 380                 l++;
 381             }
 382             l *= -1;
 383         } else {

 384             signum = 0;
 385         }
 386 
 387         // let GregorianCalendar do the heavy lifting
 388         GregorianCalendar gregorianCalendar = new GregorianCalendar(GMT);
 389 
 390         // duration is the offset from the Epoch
 391         gregorianCalendar.setTimeInMillis(l);
 392 
 393         // now find out how much each field has changed
 394         long int2long = 0L;
 395 
 396         // years
 397         int2long = gregorianCalendar.get(Calendar.YEAR) - 1970;
 398         this.years = BigInteger.valueOf(int2long);
 399 
 400         // months
 401         int2long = gregorianCalendar.get(Calendar.MONTH);
 402         this.months = BigInteger.valueOf(int2long);
 403 


 437      * the following holds for any lexically correct string x:
 438      * <pre>
 439      * new Duration(x).toString().equals(x)
 440      * </pre>
 441      *
 442      * Returns a non-null valid duration object that holds the value
 443      * indicated by the lexicalRepresentation parameter.
 444      *
 445      * @param lexicalRepresentation
 446      *      Lexical representation of a duration.
 447      * @throws IllegalArgumentException
 448      *      If the given string does not conform to the aforementioned
 449      *      specification.
 450      * @throws NullPointerException
 451      *      If the given string is null.
 452      */
 453     protected DurationImpl(String lexicalRepresentation)
 454         throws IllegalArgumentException {
 455         // only if I could use the JDK1.4 regular expression ....
 456 




 457         final String s = lexicalRepresentation;
 458         boolean positive;
 459         int[] idx = new int[1];
 460         int length = s.length();
 461         boolean timeRequired = false;
 462 
 463         if (lexicalRepresentation == null) {
 464             throw new NullPointerException();
 465         }
 466 
 467         idx[0] = 0;
 468         if (length != idx[0] && s.charAt(idx[0]) == '-') {
 469             idx[0]++;
 470             positive = false;
 471         } else {

 472             positive = true;
 473         }
 474 
 475         if (length != idx[0] && s.charAt(idx[0]++) != 'P') {
 476             throw new IllegalArgumentException(s); //,idx[0]-1);
 477         }
 478 
 479 
 480         // phase 1: chop the string into chunks
 481         // (where a chunk is '<number><a symbol>'
 482         //--------------------------------------
 483         int dateLen = 0;
 484         String[] dateParts = new String[3];
 485         int[] datePartsIndex = new int[3];
 486         while (length != idx[0]
 487             && isDigit(s.charAt(idx[0]))
 488             && dateLen < 3) {
 489             datePartsIndex[dateLen] = idx[0];
 490             dateParts[dateLen++] = parsePiece(s, idx);
 491         }
 492 
 493         if (length != idx[0]) {
 494             if (s.charAt(idx[0]++) == 'T') {
 495                 timeRequired = true;
 496             } else {

 497                 throw new IllegalArgumentException(s); // ,idx[0]-1);
 498             }
 499         }
 500 
 501         int timeLen = 0;
 502         String[] timeParts = new String[3];
 503         int[] timePartsIndex = new int[3];
 504         while (length != idx[0]
 505             && isDigitOrPeriod(s.charAt(idx[0]))
 506             && timeLen < 3) {
 507             timePartsIndex[timeLen] = idx[0];
 508             timeParts[timeLen++] = parsePiece(s, idx);
 509         }
 510 
 511         if (timeRequired && timeLen == 0) {
 512             throw new IllegalArgumentException(s); // ,idx[0]);
 513         }
 514 
 515         if (length != idx[0]) {
 516             throw new IllegalArgumentException(s); // ,idx[0]);


 587      * TODO: Javadoc.
 588      *
 589      * @param whole TODO: ???
 590      * @param parts TODO: ???
 591      * @param partsIndex TODO: ???
 592      * @param len TODO: ???
 593      * @param tokens TODO: ???
 594      *
 595      * @throws IllegalArgumentException TODO: ???
 596      */
 597     private static void organizeParts(
 598         String whole,
 599         String[] parts,
 600         int[] partsIndex,
 601         int len,
 602         String tokens)
 603         throws IllegalArgumentException {
 604 
 605         int idx = tokens.length();
 606         for (int i = len - 1; i >= 0; i--) {



 607             int nidx =
 608                 tokens.lastIndexOf(
 609                     parts[i].charAt(parts[i].length() - 1),
 610                     idx - 1);
 611             if (nidx == -1) {
 612                 throw new IllegalArgumentException(whole);
 613                 // ,partsIndex[i]+parts[i].length()-1);
 614             }
 615 
 616             for (int j = nidx + 1; j < idx; j++) {
 617                 parts[j] = null;
 618             }
 619             idx = nidx;
 620             parts[idx] = parts[i];
 621             partsIndex[idx] = partsIndex[i];
 622         }
 623         for (idx--; idx >= 0; idx--) {
 624             parts[idx] = null;
 625         }
 626     }


 705          *   <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li>
 706          * </ul>
 707          *
 708          * @param duration to compare
 709          *
 710          * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
 711          *   {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER}
 712          *   or {@link DatatypeConstants#INDETERMINATE}.
 713          *
 714          * @throws UnsupportedOperationException If the underlying implementation
 715          *   cannot reasonably process the request, e.g. W3C XML Schema allows for
 716          *   arbitrarily large/small/precise values, the request may be beyond the
 717          *   implementations capability.
 718          * @throws NullPointerException if <code>duration</code> is <code>null</code>.
 719          *
 720          * @see #isShorterThan(Duration)
 721          * @see #isLongerThan(Duration)
 722          */
 723     public int compare(Duration rhs) {
 724 
 725         BigInteger maxintAsBigInteger = BigInteger.valueOf((long) Integer.MAX_VALUE);
 726         BigInteger minintAsBigInteger = BigInteger.valueOf((long) Integer.MIN_VALUE);
 727 
 728         // check for fields that are too large in this Duration
 729         if (years != null && years.compareTo(maxintAsBigInteger) == 1) {
 730             throw new UnsupportedOperationException(
 731                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 732                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), years.toString()})
 733                                         //this.getClass().getName() + "#compare(Duration duration)"
 734                                                 //+ " years too large to be supported by this implementation "
 735                                                 //+ years.toString()
 736                                         );
 737         }
 738         if (months != null && months.compareTo(maxintAsBigInteger) == 1) {
 739                 throw new UnsupportedOperationException(
 740                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 741                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), months.toString()})
 742 
 743                                         //this.getClass().getName() + "#compare(Duration duration)"
 744                                                 //+ " months too large to be supported by this implementation "
 745                                                 //+ months.toString()
 746                                         );


 761                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), hours.toString()})
 762 
 763                                         //this.getClass().getName() + "#compare(Duration duration)"
 764                                                 //+ " hours too large to be supported by this implementation "
 765                                                 //+ hours.toString()
 766                                         );
 767         }
 768         if (minutes != null && minutes.compareTo(maxintAsBigInteger) == 1) {
 769                 throw new UnsupportedOperationException(
 770                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 771                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), minutes.toString()})
 772 
 773                                         //this.getClass().getName() + "#compare(Duration duration)"
 774                                                 //+ " minutes too large to be supported by this implementation "
 775                                                 //+ minutes.toString()
 776                                         );
 777         }
 778         if (seconds != null && seconds.toBigInteger().compareTo(maxintAsBigInteger) == 1) {
 779                 throw new UnsupportedOperationException(
 780                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 781                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), seconds.toString()})
 782 
 783                                         //this.getClass().getName() + "#compare(Duration duration)"
 784                                                 //+ " seconds too large to be supported by this implementation "
 785                                                 //+ seconds.toString()
 786                                         );
 787         }
 788 
 789         // check for fields that are too large in rhs Duration
 790         BigInteger rhsYears = (BigInteger) rhs.getField(DatatypeConstants.YEARS);
 791         if (rhsYears != null && rhsYears.compareTo(maxintAsBigInteger) == 1) {
 792                 throw new UnsupportedOperationException(
 793                         DatatypeMessageFormatter.formatMessage(null, "TooLarge",
 794                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), rhsYears.toString()})
 795 
 796                                         //this.getClass().getName() + "#compare(Duration duration)"
 797                                                 //+ " years too large to be supported by this implementation "
 798                                                 //+ rhsYears.toString()
 799                                         );
 800         }
 801         BigInteger rhsMonths = (BigInteger) rhs.getField(DatatypeConstants.MONTHS);


 940 
 941         tempA.add(duration1);
 942         tempB.add(duration2);
 943         resultB = tempA.compare(tempB);
 944         resultA = compareResults(resultA, resultB);
 945         if (resultA == DatatypeConstants.INDETERMINATE) {
 946             return DatatypeConstants.INDETERMINATE;
 947         }
 948 
 949         tempA = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 950         tempB = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 951 
 952         tempA.add(duration1);
 953         tempB.add(duration2);
 954         resultB = tempA.compare(tempB);
 955         resultA = compareResults(resultA, resultB);
 956 
 957         return resultA;
 958     }
 959 
 960     private int compareResults(int resultA, int resultB){
 961 
 962       if ( resultB == DatatypeConstants.INDETERMINATE ) {
 963             return DatatypeConstants.INDETERMINATE;
 964         }
 965         else if ( resultA!=resultB) {
 966             return DatatypeConstants.INDETERMINATE;
 967         }
 968         return resultA;
 969     }
 970 
 971     /**
 972      * Returns a hash code consistent with the definition of the equals method.
 973      *
 974      * @see Object#hashCode()
 975      */
 976     public int hashCode() {
 977         // component wise hash is not correct because 1day = 24hours
 978         Calendar cal = TEST_POINTS[0].toGregorianCalendar();
 979         this.addTo(cal);
 980         return (int) getCalendarTimeInMillis(cal);


 990      * the {@link #DurationImpl(String)} constructor.
 991      *
 992      * <p>
 993      * Formally, the following holds for any {@link Duration}
 994      * object x.
 995      * <pre>
 996      * new Duration(x.toString()).equals(x)
 997      * </pre>
 998      *
 999      * @return
1000      *      Always return a non-null valid String object.
1001      */
1002     public String toString() {
1003         StringBuffer buf = new StringBuffer();
1004         if (signum < 0) {
1005             buf.append('-');
1006         }
1007         buf.append('P');
1008 
1009         if (years != null) {
1010             buf.append(years + "Y");
1011         }
1012         if (months != null) {
1013             buf.append(months + "M");
1014         }
1015         if (days != null) {
1016             buf.append(days + "D");
1017         }
1018 
1019         if (hours != null || minutes != null || seconds != null) {
1020             buf.append('T');
1021             if (hours != null) {
1022                 buf.append(hours + "H");
1023             }
1024             if (minutes != null) {
1025                 buf.append(minutes + "M");
1026             }
1027             if (seconds != null) {
1028                 buf.append(toString(seconds) + "S");
1029             }
1030         }
1031 
1032         return buf.toString();
1033     }
1034 
1035     /**
1036      * <p>Turns {@link BigDecimal} to a string representation.</p>
1037      *
1038      * <p>Due to a behavior change in the {@link BigDecimal#toString()}
1039      * method in JDK1.5, this had to be implemented here.</p>
1040      *
1041      * @param bd <code>BigDecimal</code> to format as a <code>String</code>
1042      *
1043      * @return  <code>String</code> representation of <code>BigDecimal</code>
1044      */
1045     private String toString(BigDecimal bd) {
1046         String intString = bd.unscaledValue().toString();
1047         int scale = bd.scale();
1048 
1049         if (scale == 0) {
1050             return intString;
1051         }
1052 
1053         /* Insert decimal point */
1054         StringBuffer buf;
1055         int insertionPoint = intString.length() - scale;
1056         if (insertionPoint == 0) { /* Point goes right before intVal */
1057             return "0." + intString;
1058         } else if (insertionPoint > 0) { /* Point goes inside intVal */

1059             buf = new StringBuffer(intString);
1060             buf.insert(insertionPoint, '.');
1061         } else { /* We must insert zeros between point and intVal */

1062             buf = new StringBuffer(3 - insertionPoint + intString.length());
1063             buf.append("0.");
1064             for (int i = 0; i < -insertionPoint; i++) {
1065                 buf.append('0');
1066             }
1067             buf.append(intString);
1068         }
1069         return buf.toString();
1070     }
1071 
1072     /**
1073      * Checks if a field is set.
1074      *
1075      * A field of a duration object may or may not be present.
1076      * This method can be used to test if a field is present.
1077      *
1078      * @param field
1079      *      one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1080      *      MINUTES, or SECONDS.)
1081      * @return


1285      *   will be discarded (for example, if the actual value is 2.5,
1286      *   this method returns 2)
1287      */
1288     public int getSeconds() {
1289         return getInt(DatatypeConstants.SECONDS);
1290     }
1291 
1292     /**
1293      * <p>Return the requested field value as an int.</p>
1294      *
1295      * <p>If field is not set, i.e. == null, 0 is returned.</p>
1296      *
1297      * @param field To get value for.
1298      *
1299      * @return int value of field or 0 if field is not set.
1300      */
1301     private int getInt(DatatypeConstants.Field field) {
1302         Number n = getField(field);
1303         if (n == null) {
1304             return 0;
1305         } else {

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


1530     public Duration multiply(BigDecimal factor) {
1531         BigDecimal carry = ZERO;
1532         int factorSign = factor.signum();
1533         factor = factor.abs();
1534 
1535         BigDecimal[] buf = new BigDecimal[6];
1536 
1537         for (int i = 0; i < 5; i++) {
1538             BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);
1539             bd = bd.multiply(factor).add(carry);
1540 
1541             buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);
1542 
1543             bd = bd.subtract(buf[i]);
1544             if (i == 1) {
1545                 if (bd.signum() != 0) {
1546                     throw new IllegalStateException(); // illegal carry-down
1547                 } else {
1548                     carry = ZERO;
1549                 }
1550             } else {

1551                 carry = bd.multiply(FACTORS[i]);
1552             }
1553         }
1554 
1555         if (seconds != null) {
1556             buf[5] = seconds.multiply(factor).add(carry);
1557         } else {

1558             buf[5] = carry;
1559         }
1560 
1561         return new DurationImpl(
1562             this.signum * factorSign >= 0,
1563             toBigInteger(buf[0], null == years),
1564             toBigInteger(buf[1], null == months),
1565             toBigInteger(buf[2], null == days),
1566             toBigInteger(buf[3], null == hours),
1567             toBigInteger(buf[4], null == minutes),
1568             (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
1569     }
1570 
1571     /**
1572      * <p>Gets the value of the field as a {@link BigDecimal}.</p>
1573      *
1574      * <p>If the field is unset, return 0.</p>
1575      *
1576      * @param f Field to get value for.
1577      *
1578      * @return  non-null valid {@link BigDecimal}.
1579      */
1580     private BigDecimal getFieldAsBigDecimal(DatatypeConstants.Field f) {
1581         if (f == DatatypeConstants.SECONDS) {
1582             if (seconds != null) {
1583                 return seconds;
1584             } else {

1585                 return ZERO;
1586             }
1587         } else {

1588             BigInteger bi = (BigInteger) getField(f);
1589             if (bi == null) {
1590                 return ZERO;
1591             } else {

1592                 return new BigDecimal(bi);
1593             }
1594         }
1595     }
1596 
1597     /**
1598      * <p>BigInteger value of BigDecimal value.</p>
1599      *
1600      * @param value Value to convert.
1601      * @param canBeNull Can returned value be null?
1602      *
1603      * @return BigInteger value of BigDecimal, possibly null.
1604      */
1605     private static BigInteger toBigInteger(
1606         BigDecimal value,
1607         boolean canBeNull) {
1608         if (canBeNull && value.signum() == 0) {
1609             return null;
1610         } else {

1611             return value.unscaledValue();
1612         }
1613     }
1614 
1615     /**
1616      * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
1617      * FIELDS[i+1].
1618      */
1619     private static final BigDecimal[] FACTORS = new BigDecimal[]{
1620         BigDecimal.valueOf(12),
1621         null/*undefined*/,
1622         BigDecimal.valueOf(24),
1623         BigDecimal.valueOf(60),
1624         BigDecimal.valueOf(60)
1625     };
1626 
1627     /**
1628      * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
1629      *
1630      * <p>For example,</p>
1631      * <pre>
1632      * "1 day" + "-3 days" = "-2 days"
1633      * "1 year" + "1 day" = "1 year and 1 day"
1634      * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
1635      * "15 hours" + "-3 days" = "-(2 days,9 hours)"
1636      * "1 year" + "-1 day" = IllegalStateException
1637      * </pre>
1638      *
1639      * <p>Since there's no way to meaningfully subtract 1 day from 1 month,


1947      * The updated time instant is then converted back into a
1948      * {@link Date} object and used to update the given {@link Date} object.
1949      *
1950      * <p>
1951      * This somewhat redundant computation is necessary to unambiguously
1952      * determine the duration of months and years.
1953      *
1954      * @param date
1955      *      A date object whose value will be modified.
1956      * @throws NullPointerException
1957      *      if the date parameter is null.
1958      */
1959     public void addTo(Date date) {
1960         Calendar cal = new GregorianCalendar();
1961         cal.setTime(date); // this will throw NPE if date==null
1962         this.addTo(cal);
1963         date.setTime(getCalendarTimeInMillis(cal));
1964     }
1965 
1966     /**











1967      * <p>Stream Unique Identifier.</p>
1968      *
1969      * <p>TODO: Serialization should use the XML string representation as
1970      * the serialization format to ensure future compatibility.</p>
1971      */
1972     private static final long serialVersionUID = 1L;
1973 
1974     /**
1975      * Writes {@link Duration} as a lexical representation
1976      * for maximum future compatibility.
1977      *
1978      * @return
1979      *      An object that encapsulates the string
1980      *      returned by <code>this.toString()</code>.
1981      */
1982     private Object writeReplace() throws IOException {
1983         return new DurationStream(this.toString());
1984     }
1985 
1986     /**
1987      * Representation of {@link Duration} in the object stream.
1988      *
1989      * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
1990      */
1991     private static class DurationStream implements Serializable {
1992         private final String lexical;
1993 
1994         private DurationStream(String _lexical) {
1995             this.lexical = _lexical;
1996         }
1997 
1998         private Object readResolve() throws ObjectStreamException {
1999             //            try {
2000             return new DurationImpl(lexical);
2001             //            } catch( ParseException e ) {
2002             //                throw new StreamCorruptedException("unable to parse "+lexical+" as duration");
2003             //            }
2004         }
2005 
2006         private static final long serialVersionUID = 1L;
2007     }
2008 
2009     /**
2010      * Calls the {@link Calendar#getTimeInMillis} method.
2011      * Prior to JDK1.4, this method was protected and therefore
2012      * cannot be invoked directly.
2013      *
2014      * In future, this should be replaced by
2015      * <code>cal.getTimeInMillis()</code>
2016      */
2017     private static long getCalendarTimeInMillis(Calendar cal) {
2018         return cal.getTime().getTime();
2019     }
2020 }
   1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  * 




  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  * 
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xerces.internal.jaxp.datatype;
  23 
  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.util.Calendar;
  30 import java.util.Date;
  31 import java.util.GregorianCalendar;
  32 import java.util.TimeZone;


  90  *
  91  * 
  92  * <h2>Range of allowed values</h2>
  93  * <p>
  94  * Because some operations of {@link Duration} rely on {@link Calendar}
  95  * even though {@link Duration} can hold very large or very small values,
  96  * some of the methods may not work correctly on such {@link Duration}s.
  97  * The impacted methods document their dependency on {@link Calendar}.
  98  * 
  99  *  
 100  * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
 101  * @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
 102  * @version $Revision: 1.8 $, $Date: 2010/05/19 23:20:06 $
 103 
 104  * @see XMLGregorianCalendar#add(Duration)
 105  */
 106 class DurationImpl
 107         extends Duration
 108         implements Serializable {
 109     




 110 
 111     /**
 112      * <p>Internal array of value Fields.</p>
 113      */
 114     private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
 115         DatatypeConstants.YEARS,
 116         DatatypeConstants.MONTHS,
 117         DatatypeConstants.DAYS,
 118         DatatypeConstants.HOURS,
 119         DatatypeConstants.MINUTES,
 120         DatatypeConstants.SECONDS
 121     };
 122 
 123     /**
 124                  * <p>Internal array of value Field ids.</p>
 125                  */
 126                 private static final int[] FIELD_IDS = {
 127                                 DatatypeConstants.YEARS.getId(),
 128                                 DatatypeConstants.MONTHS.getId(),
 129                                 DatatypeConstants.DAYS.getId(),
 130                                 DatatypeConstants.HOURS.getId(),
 131                                 DatatypeConstants.MINUTES.getId(),
 132                                 DatatypeConstants.SECONDS.getId()
 133                         };
 134 
 135     /**
 136      * TimeZone for GMT.
 137      */
 138     private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
 139 
 140         /**
 141      * <p>BigDecimal value of 0.</p>
 142      */    
 143     private static final BigDecimal ZERO = BigDecimal.valueOf(0);
 144 
 145     /**
 146      * <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
 147      * zero, or positive.</p>
 148      */
 149     protected int signum;
 150 
 151     /**
 152      * <p>Years of this <code>Duration</code>.</p>
 153      */
 154     /**
 155      * These were final since Duration is immutable. But new subclasses need
 156      * to be able to set after conversion. It won't break the immutable nature
 157      * of them since there's no other way to set new values to them
 158      */
 159     protected BigInteger years;
 160 
 161     /**
 162      * <p>Months of this <code>Duration</code>.</p>
 163      */


 196     }
 197 
 198         /**
 199          * TODO: Javadoc
 200          * @param isPositive Sign.
 201          * 
 202          * @return 1 if positive, else -1.
 203          */             
 204     protected int calcSignum(boolean isPositive) {
 205         if ((years == null || years.signum() == 0)
 206              && (months == null || months.signum() == 0)
 207              && (days == null || days.signum() == 0)
 208              && (hours == null || hours.signum() == 0)
 209              && (minutes == null || minutes.signum() == 0)
 210              && (seconds == null || seconds.signum() == 0)) {
 211             return 0;
 212         }
 213 
 214         if (isPositive) {
 215             return 1;
 216         } 
 217         else {
 218             return -1;
 219         }

 220     }
 221     
 222     /**
 223      * <p>Constructs a new Duration object by specifying each field individually.</p>
 224      * 
 225      * <p>All the parameters are optional as long as at least one field is present.
 226      * If specified, parameters have to be zero or positive.</p>
 227      * 
 228      * @param isPositive Set to <code>false</code> to create a negative duration. When the length
 229      *   of the duration is zero, this parameter will be ignored.
 230      * @param years of this <code>Duration</code>
 231      * @param months of this <code>Duration</code>
 232      * @param days of this <code>Duration</code>
 233      * @param hours of this <code>Duration</code>
 234      * @param minutes of this <code>Duration</code>
 235      * @param seconds of this <code>Duration</code>
 236      * 
 237      * @throws IllegalArgumentException
 238      *    If years, months, days, hours, minutes and
 239      *    seconds parameters are all <code>null</code>. Or if any


 337             wrap(hours),
 338             wrap(minutes),
 339             seconds != DatatypeConstants.FIELD_UNDEFINED ? new BigDecimal(String.valueOf(seconds)) : null);
 340     }
 341 
 342         /**
 343          * TODO: Javadoc
 344          * 
 345          * @param i int to convert to BigInteger.
 346          * 
 347          * @return BigInteger representation of int.
 348          */
 349     protected static BigInteger wrap(final int i) {
 350 
 351         // field may not be set
 352         if (i == DatatypeConstants.FIELD_UNDEFINED) {
 353                 return null;
 354         }
 355         
 356         // int -> BigInteger
 357         return BigInteger.valueOf(i);
 358     }
 359     
 360     /**
 361      * <p>Constructs a new Duration object by specifying the duration
 362      * in milliseconds.</p>
 363      * 
 364      * @param durationInMilliSeconds
 365      *      The length of the duration in milliseconds.
 366      */
 367     protected DurationImpl(final long durationInMilliSeconds) {
 368 
 369         long l = durationInMilliSeconds;
 370 
 371         if (l > 0) {
 372             signum = 1;
 373         } 
 374         else if (l < 0) {
 375             signum = -1;
 376             if (l == 0x8000000000000000L) {
 377                 // negating 0x8000000000000000L causes an overflow
 378                 l++;
 379             }
 380             l *= -1;
 381         } 
 382         else {
 383             signum = 0;
 384         }
 385 
 386         // let GregorianCalendar do the heavy lifting
 387         GregorianCalendar gregorianCalendar = new GregorianCalendar(GMT);
 388 
 389         // duration is the offset from the Epoch
 390         gregorianCalendar.setTimeInMillis(l);
 391 
 392         // now find out how much each field has changed
 393         long int2long = 0L;
 394 
 395         // years
 396         int2long = gregorianCalendar.get(Calendar.YEAR) - 1970;
 397         this.years = BigInteger.valueOf(int2long);
 398 
 399         // months
 400         int2long = gregorianCalendar.get(Calendar.MONTH);
 401         this.months = BigInteger.valueOf(int2long);
 402 


 436      * the following holds for any lexically correct string x:
 437      * <pre>
 438      * new Duration(x).toString().equals(x)
 439      * </pre>
 440      * 
 441      * Returns a non-null valid duration object that holds the value
 442      * indicated by the lexicalRepresentation parameter.
 443      *
 444      * @param lexicalRepresentation
 445      *      Lexical representation of a duration.
 446      * @throws IllegalArgumentException
 447      *      If the given string does not conform to the aforementioned
 448      *      specification.
 449      * @throws NullPointerException
 450      *      If the given string is null.
 451      */
 452     protected DurationImpl(String lexicalRepresentation)
 453         throws IllegalArgumentException {
 454         // only if I could use the JDK1.4 regular expression ....
 455 
 456         if (lexicalRepresentation == null) {
 457            throw new NullPointerException();
 458         }
 459         
 460         final String s = lexicalRepresentation;
 461         boolean positive;
 462         int[] idx = new int[1];
 463         int length = s.length();
 464         boolean timeRequired = false;
 465 




 466         idx[0] = 0;
 467         if (length != idx[0] && s.charAt(idx[0]) == '-') {
 468             idx[0]++;
 469             positive = false;
 470         } 
 471         else {
 472             positive = true;
 473         }
 474 
 475         if (length != idx[0] && s.charAt(idx[0]++) != 'P') {
 476             throw new IllegalArgumentException(s); //,idx[0]-1);
 477         }
 478 
 479 
 480         // phase 1: chop the string into chunks
 481         // (where a chunk is '<number><a symbol>'
 482         //--------------------------------------
 483         int dateLen = 0;
 484         String[] dateParts = new String[3];
 485         int[] datePartsIndex = new int[3];
 486         while (length != idx[0]
 487                && isDigit(s.charAt(idx[0]))
 488                && dateLen < 3) {
 489             datePartsIndex[dateLen] = idx[0];
 490             dateParts[dateLen++] = parsePiece(s, idx);
 491         }
 492 
 493         if (length != idx[0]) {
 494             if (s.charAt(idx[0]++) == 'T') {
 495                 timeRequired = true;
 496             } 
 497             else {
 498                 throw new IllegalArgumentException(s); // ,idx[0]-1);
 499             }
 500         }
 501 
 502         int timeLen = 0;
 503         String[] timeParts = new String[3];
 504         int[] timePartsIndex = new int[3];
 505         while (length != idx[0]
 506                              && isDigitOrPeriod(s.charAt(idx[0]))
 507                              && timeLen < 3) {
 508             timePartsIndex[timeLen] = idx[0];
 509             timeParts[timeLen++] = parsePiece(s, idx);
 510         }
 511 
 512         if (timeRequired && timeLen == 0) {
 513             throw new IllegalArgumentException(s); // ,idx[0]);
 514         }
 515 
 516         if (length != idx[0]) {
 517             throw new IllegalArgumentException(s); // ,idx[0]);


 588      * TODO: Javadoc.
 589      * 
 590      * @param whole TODO: ???
 591      * @param parts TODO: ???
 592      * @param partsIndex TODO: ???
 593      * @param len TODO: ???
 594      * @param tokens TODO: ???
 595      * 
 596      * @throws IllegalArgumentException TODO: ???
 597      */
 598     private static void organizeParts(
 599         String whole,
 600         String[] parts,
 601         int[] partsIndex,
 602         int len,
 603         String tokens)
 604         throws IllegalArgumentException {
 605 
 606         int idx = tokens.length();
 607         for (int i = len - 1; i >= 0; i--) {
 608             if (parts[i] == null) {
 609                 throw new IllegalArgumentException(whole);
 610             }
 611             int nidx =
 612                 tokens.lastIndexOf(
 613                     parts[i].charAt(parts[i].length() - 1),
 614                     idx - 1);
 615             if (nidx == -1) {
 616                 throw new IllegalArgumentException(whole);
 617                 // ,partsIndex[i]+parts[i].length()-1);
 618             }
 619 
 620             for (int j = nidx + 1; j < idx; j++) {
 621                 parts[j] = null;
 622             }
 623             idx = nidx;
 624             parts[idx] = parts[i];
 625             partsIndex[idx] = partsIndex[i];
 626         }
 627         for (idx--; idx >= 0; idx--) {
 628             parts[idx] = null;
 629         }
 630     }


 709          *   <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li>
 710          * </ul>
 711          *
 712          * @param duration to compare
 713          * 
 714          * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
 715          *   {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER}
 716          *   or {@link DatatypeConstants#INDETERMINATE}.
 717          * 
 718          * @throws UnsupportedOperationException If the underlying implementation
 719          *   cannot reasonably process the request, e.g. W3C XML Schema allows for
 720          *   arbitrarily large/small/precise values, the request may be beyond the
 721          *   implementations capability.
 722          * @throws NullPointerException if <code>duration</code> is <code>null</code>. 
 723          *
 724          * @see #isShorterThan(Duration)
 725          * @see #isLongerThan(Duration)
 726          */
 727     public int compare(Duration rhs) {
 728         
 729         BigInteger maxintAsBigInteger = BigInteger.valueOf(Integer.MAX_VALUE);

 730 
 731         // check for fields that are too large in this Duration
 732         if (years != null && years.compareTo(maxintAsBigInteger) == 1) {                
 733             throw new UnsupportedOperationException(
 734                         DatatypeMessageFormatter.formatMessage(null, "TooLarge", 
 735                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), years.toString()})
 736                                         //this.getClass().getName() + "#compare(Duration duration)"
 737                                                 //+ " years too large to be supported by this implementation "
 738                                                 //+ years.toString()
 739                                         );
 740         }
 741         if (months != null && months.compareTo(maxintAsBigInteger) == 1) {
 742                 throw new UnsupportedOperationException(
 743                         DatatypeMessageFormatter.formatMessage(null, "TooLarge", 
 744                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), months.toString()})
 745             
 746                                         //this.getClass().getName() + "#compare(Duration duration)"
 747                                                 //+ " months too large to be supported by this implementation "
 748                                                 //+ months.toString()
 749                                         );


 764                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), hours.toString()})
 765             
 766                                         //this.getClass().getName() + "#compare(Duration duration)"
 767                                                 //+ " hours too large to be supported by this implementation "
 768                                                 //+ hours.toString()
 769                                         );
 770         }
 771         if (minutes != null && minutes.compareTo(maxintAsBigInteger) == 1) {
 772                 throw new UnsupportedOperationException(
 773                         DatatypeMessageFormatter.formatMessage(null, "TooLarge", 
 774                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), minutes.toString()})
 775             
 776                                         //this.getClass().getName() + "#compare(Duration duration)"
 777                                                 //+ " minutes too large to be supported by this implementation "
 778                                                 //+ minutes.toString()
 779                                         );
 780         }
 781         if (seconds != null && seconds.toBigInteger().compareTo(maxintAsBigInteger) == 1) {
 782                 throw new UnsupportedOperationException(
 783                         DatatypeMessageFormatter.formatMessage(null, "TooLarge", 
 784                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), toString(seconds)})
 785             
 786                                         //this.getClass().getName() + "#compare(Duration duration)"
 787                                                 //+ " seconds too large to be supported by this implementation "
 788                                                 //+ seconds.toString()
 789                                         );
 790         }
 791         
 792         // check for fields that are too large in rhs Duration
 793         BigInteger rhsYears = (BigInteger) rhs.getField(DatatypeConstants.YEARS);
 794         if (rhsYears != null && rhsYears.compareTo(maxintAsBigInteger) == 1) {
 795                 throw new UnsupportedOperationException(
 796                         DatatypeMessageFormatter.formatMessage(null, "TooLarge", 
 797                             new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), rhsYears.toString()})
 798             
 799                                         //this.getClass().getName() + "#compare(Duration duration)"
 800                                                 //+ " years too large to be supported by this implementation "
 801                                                 //+ rhsYears.toString()
 802                                         );
 803         }
 804         BigInteger rhsMonths = (BigInteger) rhs.getField(DatatypeConstants.MONTHS);


 943         
 944         tempA.add(duration1);
 945         tempB.add(duration2);
 946         resultB = tempA.compare(tempB);
 947         resultA = compareResults(resultA, resultB);
 948         if (resultA == DatatypeConstants.INDETERMINATE) {
 949             return DatatypeConstants.INDETERMINATE;
 950         }
 951 
 952         tempA = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 953         tempB = (XMLGregorianCalendar)TEST_POINTS[3].clone();
 954         
 955         tempA.add(duration1);
 956         tempB.add(duration2);
 957         resultB = tempA.compare(tempB);
 958         resultA = compareResults(resultA, resultB);
 959 
 960         return resultA;
 961     }
 962 
 963     private int compareResults(int resultA, int resultB) {
 964 
 965         if ( resultB == DatatypeConstants.INDETERMINATE ) {
 966             return DatatypeConstants.INDETERMINATE;
 967         }
 968         else if ( resultA!=resultB) {
 969             return DatatypeConstants.INDETERMINATE;
 970         }
 971         return resultA;
 972     }
 973     
 974     /**
 975      * Returns a hash code consistent with the definition of the equals method.
 976      * 
 977      * @see Object#hashCode() 
 978      */
 979     public int hashCode() {
 980         // component wise hash is not correct because 1day = 24hours
 981         Calendar cal = TEST_POINTS[0].toGregorianCalendar();
 982         this.addTo(cal);
 983         return (int) getCalendarTimeInMillis(cal);


 993      * the {@link #DurationImpl(String)} constructor.
 994      * 
 995      * <p>
 996      * Formally, the following holds for any {@link Duration}
 997      * object x. 
 998      * <pre>
 999      * new Duration(x.toString()).equals(x)
1000      * </pre>
1001      * 
1002      * @return
1003      *      Always return a non-null valid String object.
1004      */
1005     public String toString() {
1006         StringBuffer buf = new StringBuffer();
1007         if (signum < 0) {
1008             buf.append('-');
1009         }
1010         buf.append('P');
1011         
1012         if (years != null) {
1013             buf.append(years).append('Y');
1014         }
1015         if (months != null) {
1016             buf.append(months).append('M');
1017         }
1018         if (days != null) {
1019             buf.append(days).append('D');
1020         }
1021 
1022         if (hours != null || minutes != null || seconds != null) {
1023             buf.append('T');
1024             if (hours != null) {
1025                 buf.append(hours).append('H');
1026             }
1027             if (minutes != null) {
1028                 buf.append(minutes).append('M');
1029             }
1030             if (seconds != null) {
1031                 buf.append(toString(seconds)).append('S');
1032             }
1033         }
1034         
1035         return buf.toString();
1036     }
1037 
1038     /**
1039      * <p>Turns {@link BigDecimal} to a string representation.</p>
1040      * 
1041      * <p>Due to a behavior change in the {@link BigDecimal#toString()}
1042      * method in JDK1.5, this had to be implemented here.</p>
1043      * 
1044      * @param bd <code>BigDecimal</code> to format as a <code>String</code>
1045      * 
1046      * @return  <code>String</code> representation of <code>BigDecimal</code> 
1047      */
1048     private String toString(BigDecimal bd) {
1049         String intString = bd.unscaledValue().toString();
1050         int scale = bd.scale();
1051 
1052         if (scale == 0) {
1053             return intString;
1054         }
1055 
1056         /* Insert decimal point */
1057         StringBuffer buf;
1058         int insertionPoint = intString.length() - scale;
1059         if (insertionPoint == 0) { /* Point goes right before intVal */
1060             return "0." + intString;
1061         } 
1062         else if (insertionPoint > 0) { /* Point goes inside intVal */
1063             buf = new StringBuffer(intString);
1064             buf.insert(insertionPoint, '.');
1065         } 
1066         else { /* We must insert zeros between point and intVal */
1067             buf = new StringBuffer(3 - insertionPoint + intString.length());
1068             buf.append("0.");
1069             for (int i = 0; i < -insertionPoint; i++) {
1070                 buf.append('0');
1071             }
1072             buf.append(intString);
1073         }
1074         return buf.toString();
1075     }
1076 
1077     /**
1078      * Checks if a field is set.
1079      * 
1080      * A field of a duration object may or may not be present.
1081      * This method can be used to test if a field is present.
1082      * 
1083      * @param field
1084      *      one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
1085      *      MINUTES, or SECONDS.)
1086      * @return


1290      *   will be discarded (for example, if the actual value is 2.5,
1291      *   this method returns 2)
1292      */
1293     public int getSeconds() {
1294         return getInt(DatatypeConstants.SECONDS);
1295     }
1296     
1297     /**
1298      * <p>Return the requested field value as an int.</p>
1299      * 
1300      * <p>If field is not set, i.e. == null, 0 is returned.</p>
1301      * 
1302      * @param field To get value for.
1303      * 
1304      * @return int value of field or 0 if field is not set.
1305      */
1306     private int getInt(DatatypeConstants.Field field) {
1307         Number n = getField(field);
1308         if (n == null) {
1309             return 0;
1310         } 
1311         else {
1312             return n.intValue();
1313         }
1314     }
1315         
1316     /**
1317      * <p>Returns the length of the duration in milli-seconds.</p>
1318      * 
1319      * <p>If the seconds field carries more digits than milli-second order,
1320      * those will be simply discarded (or in other words, rounded to zero.)  
1321      * For example, for any Calendar value <code>x<code>,</p>
1322      * <pre>
1323      * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1324      * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1325      * </pre>
1326      * 
1327      * <p>
1328      * Note that this method uses the {@link #addTo(Calendar)} method,
1329      * which may work incorectly with {@link Duration} objects with
1330      * very large values in its fields. See the {@link #addTo(Calendar)}
1331      * method for details.
1332      * 
1333      * @param startInstant
1334      *      The length of a month/year varies. The <code>startInstant</code> is
1335      *      used to disambiguate this variance. Specifically, this method
1336      *      returns the difference between <code>startInstant</code> and
1337      *      <code>startInstant+duration</code>
1338      * 
1339      * @return milliseconds between <code>startInstant</code> and
1340      *   <code>startInstant</code> plus this <code>Duration</code>
1341      *
1342      * @throws NullPointerException if <code>startInstant</code> parameter 
1343      * is null.
1344      * 
1345      */
1346     public long getTimeInMillis(final Calendar startInstant) {
1347         Calendar cal = (Calendar) startInstant.clone();
1348         addTo(cal);
1349         return getCalendarTimeInMillis(cal) - getCalendarTimeInMillis(startInstant);

1350     }
1351     
1352     /**
1353      * <p>Returns the length of the duration in milli-seconds.</p>
1354      * 
1355      * <p>If the seconds field carries more digits than milli-second order,
1356      * those will be simply discarded (or in other words, rounded to zero.)
1357      * For example, for any <code>Date</code> value <code>x<code>,</p>   
1358      * <pre>
1359      * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
1360      * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
1361      * </pre>
1362      * 
1363      * <p>
1364      * Note that this method uses the {@link #addTo(Date)} method,
1365      * which may work incorectly with {@link Duration} objects with
1366      * very large values in its fields. See the {@link #addTo(Date)}
1367      * method for details.
1368      * 
1369      * @param startInstant


1535     public Duration multiply(BigDecimal factor) {
1536         BigDecimal carry = ZERO;
1537         int factorSign = factor.signum();
1538         factor = factor.abs();
1539         
1540         BigDecimal[] buf = new BigDecimal[6];
1541         
1542         for (int i = 0; i < 5; i++) {
1543             BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);
1544             bd = bd.multiply(factor).add(carry);
1545             
1546             buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);
1547             
1548             bd = bd.subtract(buf[i]);
1549             if (i == 1) {
1550                 if (bd.signum() != 0) {
1551                     throw new IllegalStateException(); // illegal carry-down
1552                 } else {
1553                     carry = ZERO;
1554                 }
1555             } 
1556             else {
1557                 carry = bd.multiply(FACTORS[i]);
1558             }
1559         }
1560         
1561         if (seconds != null) {
1562             buf[5] = seconds.multiply(factor).add(carry);
1563         } 
1564         else {
1565             buf[5] = carry;
1566         }
1567                 
1568         return new DurationImpl(
1569             this.signum * factorSign >= 0,
1570             toBigInteger(buf[0], null == years),
1571             toBigInteger(buf[1], null == months),
1572             toBigInteger(buf[2], null == days),
1573             toBigInteger(buf[3], null == hours),
1574             toBigInteger(buf[4], null == minutes),
1575             (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
1576     }
1577     
1578     /**
1579      * <p>Gets the value of the field as a {@link BigDecimal}.</p>
1580      * 
1581      * <p>If the field is unset, return 0.</p>
1582      * 
1583      * @param f Field to get value for.
1584      * 
1585      * @return  non-null valid {@link BigDecimal}.
1586      */
1587     private BigDecimal getFieldAsBigDecimal(DatatypeConstants.Field f) {
1588         if (f == DatatypeConstants.SECONDS) {
1589             if (seconds != null) {
1590                 return seconds;
1591             } 
1592             else {
1593                 return ZERO;
1594             }
1595         } 
1596         else {
1597             BigInteger bi = (BigInteger) getField(f);
1598             if (bi == null) {
1599                 return ZERO;
1600             } 
1601             else {
1602                 return new BigDecimal(bi);
1603             }
1604         }
1605     }
1606     
1607     /**
1608      * <p>BigInteger value of BigDecimal value.</p>
1609      * 
1610      * @param value Value to convert.
1611      * @param canBeNull Can returned value be null?
1612      * 
1613      * @return BigInteger value of BigDecimal, possibly null.
1614      */
1615     private static BigInteger toBigInteger(
1616         BigDecimal value,
1617         boolean canBeNull) {
1618         if (canBeNull && value.signum() == 0) {
1619             return null;
1620         } 
1621         else {
1622             return value.unscaledValue();
1623         }
1624     }
1625     
1626     /**
1627      * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
1628      * FIELDS[i+1].
1629      */
1630     private static final BigDecimal[] FACTORS = new BigDecimal[] {
1631         BigDecimal.valueOf(12),
1632         null/*undefined*/,
1633         BigDecimal.valueOf(24),
1634         BigDecimal.valueOf(60),
1635         BigDecimal.valueOf(60)
1636     };    
1637     
1638     /**
1639      * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
1640      * 
1641      * <p>For example,</p>
1642      * <pre>
1643      * "1 day" + "-3 days" = "-2 days"
1644      * "1 year" + "1 day" = "1 year and 1 day"
1645      * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
1646      * "15 hours" + "-3 days" = "-(2 days,9 hours)"
1647      * "1 year" + "-1 day" = IllegalStateException
1648      * </pre>
1649      * 
1650      * <p>Since there's no way to meaningfully subtract 1 day from 1 month,


1958      * The updated time instant is then converted back into a
1959      * {@link Date} object and used to update the given {@link Date} object.
1960      * 
1961      * <p>
1962      * This somewhat redundant computation is necessary to unambiguously
1963      * determine the duration of months and years.
1964      * 
1965      * @param date
1966      *      A date object whose value will be modified.
1967      * @throws NullPointerException
1968      *      if the date parameter is null.
1969      */
1970     public void addTo(Date date) {
1971         Calendar cal = new GregorianCalendar();
1972         cal.setTime(date); // this will throw NPE if date==null
1973         this.addTo(cal);
1974         date.setTime(getCalendarTimeInMillis(cal));
1975     }
1976     
1977     /**
1978      * Returns time value in milliseconds
1979      * @param cal A calendar object
1980      * @return time value
1981      * 
1982      * Diff from Xerces; Use JDK 1.5 feature.
1983      */
1984     private static long getCalendarTimeInMillis(Calendar cal) {
1985         return cal.getTimeInMillis();
1986     }
1987     
1988     /**
1989      * <p>Stream Unique Identifier.</p>
1990      * 
1991      * <p>Serialization uses the lexical form returned by toString().</p>

1992      */
1993     private static final long serialVersionUID = 1L;
1994     
1995     /**
1996      * Writes {@link Duration} as a lexical representation
1997      * for maximum future compatibility.
1998      * 
1999      * @return
2000      *      An object that encapsulates the string
2001      *      returned by <code>this.toString()</code>.
2002      */
2003     private Object writeReplace() throws IOException {
2004         return new DurationStream(this.toString());
2005     }
2006 
2007     /**
2008      * Representation of {@link Duration} in the object stream.
2009      *
2010      * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
2011      */
2012     private static class DurationStream implements Serializable {
2013         private final String lexical;
2014 
2015         private DurationStream(String _lexical) {
2016             this.lexical = _lexical;
2017         }
2018 
2019         private Object readResolve() throws ObjectStreamException {

2020             return new DurationImpl(lexical);



2021         }
2022 
2023         private static final long serialVersionUID = 1L;
2024     }
2025 











2026 }