Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]

    1   /*   Copyright 2004 The Apache Software Foundation
    2    *
    3    *   Licensed under the Apache License, Version 2.0 (the "License");
    4    *   you may not use this file except in compliance with the License.
    5    *   You may obtain a copy of the License at
    6    *
    7    *       http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    *   Unless required by applicable law or agreed to in writing, software
   10    *   distributed under the License is distributed on an "AS IS" BASIS,
   11    *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12    *   See the License for the specific language governing permissions and
   13    *  limitations under the License.
   14    */
   15   
   16   package org.apache.xmlbeans;
   17   
   18   import java.math.BigDecimal;
   19   import java.math.BigInteger;
   20   
   21   /**
   22    * Used to build {@link GDuration GDurations}.
   23    */ 
   24   public class GDurationBuilder implements GDurationSpecification, java.io.Serializable
   25   {
   26       private static final long serialVersionUID = 1L;
   27       
   28       private int _sign;
   29       private int _CY;
   30       private int _M;
   31       private int _D;
   32       private int _h;
   33       private int _m;
   34       private int _s;
   35       private BigDecimal _fs;
   36   
   37       /**
   38        * Constructs an empty GDurationBuilder representing zero seconds.
   39        */
   40       public GDurationBuilder()
   41       {
   42           _sign = +1;
   43           _fs = GDate._zero;
   44       }
   45   
   46       /**
   47        * Constructs a GDuration from a lexical
   48        * representation.
   49        */
   50       public GDurationBuilder(String s)
   51       {
   52           this(new GDuration(s));
   53       }
   54   
   55       /**
   56        * Constructs a GDurationBuilder with the specified sign,
   57        * year, month, day, hours, minutes, seconds, and optional
   58        * fractional seconds.
   59        * @param sign +1 for a positive duration, -1 for a negative duration
   60        * @throws java.lang.IllegalArgumentException if the sign is not 1 or -1
   61        */
   62       public GDurationBuilder(
   63               int sign,
   64               int year,
   65               int month,
   66               int day,
   67               int hour,
   68               int minute,
   69               int second,
   70               BigDecimal fraction)
   71       {
   72           if (sign != 1 && sign != -1)
   73               throw new IllegalArgumentException();
   74           _sign = sign;
   75           _CY = year;
   76           _M = month;
   77           _D = day;
   78           _h = hour;
   79           _m = minute;
   80           _s = second;
   81           _fs = fraction == null ? GDate._zero : fraction;
   82       }
   83   
   84       /**
   85        * Constructs a GDurationBuilder from another GDurationBuilderSpecification.
   86        */
   87       public GDurationBuilder(GDurationSpecification gDuration)
   88       {
   89           _sign = gDuration.getSign();
   90           _CY = gDuration.getYear();
   91           _M = gDuration.getMonth();
   92           _D = gDuration.getDay();
   93           _h = gDuration.getHour();
   94           _m = gDuration.getMinute();
   95           _s = gDuration.getSecond();
   96           _fs = gDuration.getFraction();
   97       }
   98   
   99       /**
  100        * Builds another GDurationBuilder with the same value
  101        * as this one.
  102        */
  103       public Object clone()
  104       {
  105           return new GDurationBuilder(this);
  106       }
  107   
  108       /**
  109        * Builds a GDuration from this GDurationBuilder.
  110        */
  111       public GDuration toGDuration()
  112       {
  113           return new GDuration(this);
  114       }
  115   
  116       /**
  117        * Adds to this duration.  Does a fieldwise add, with no
  118        * normalization.
  119        */
  120       public void addGDuration(GDurationSpecification duration)
  121       {
  122           int sign = _sign * duration.getSign();
  123           _add(duration, sign);
  124       }
  125   
  126       /**
  127        * Subtracts from this duration.  Does a fieldwise subtraction,
  128        * with no normalization.
  129        */
  130       public void subtractGDuration(GDurationSpecification duration)
  131       {
  132           int sign = -_sign * duration.getSign();
  133           _add(duration, sign);
  134       }
  135   
  136       private void _add(GDurationSpecification duration, int sign)
  137       {
  138           _CY += sign * duration.getYear();
  139           _M += sign * duration.getMonth();
  140           _D += sign * duration.getDay();
  141           _h += sign * duration.getHour();
  142           _m += sign * duration.getMinute();
  143           _s += sign * duration.getSecond();
  144   
  145           if (duration.getFraction().signum() == 0)
  146               return;
  147   
  148           if (_fs.signum() == 0 && sign == 1)
  149               _fs = duration.getFraction();
  150           else
  151               _fs = sign > 0 ?
  152                       _fs.add(duration.getFraction()) :
  153                       _fs.subtract(duration.getFraction());
  154       }
  155   
  156       /**
  157        * Sets the sign.
  158        */
  159       public final void setSign(int sign)
  160       {
  161           if (sign != 1 && sign != -1)
  162               throw new IllegalArgumentException();
  163           _sign = sign;
  164       }
  165   
  166       /**
  167        * Sets the year component.
  168        */
  169       public void setYear(int year)
  170           { _CY = year; }
  171   
  172       /**
  173        * Sets the month component.
  174        */
  175       public void setMonth(int month)
  176           { _M = month; }
  177   
  178       /**
  179        * Sets the day component.
  180        */
  181       public void setDay(int day)
  182           { _D = day;  }
  183   
  184       /**
  185        * Sets the hour component.
  186        */
  187       public void setHour(int hour)
  188           { _h = hour;  }
  189   
  190       /**
  191        * Sets the minute component.
  192        */
  193       public void setMinute(int minute)
  194           { _m = minute; }
  195   
  196       /**
  197        * Sets the second component.
  198        */
  199       public void setSecond(int second)
  200           { _s = second; }
  201   
  202       /**
  203        * Sets the fraction-of-second component.
  204        */
  205       public void setFraction(BigDecimal fraction)
  206           { _fs = fraction == null ? GDate._zero : fraction; }
  207   
  208       /**
  209        * All GDuration instances return true.
  210        */
  211       public final boolean isImmutable()
  212       {
  213           return true;
  214       }
  215   
  216       /**
  217        * Returns the sign of the duration: +1 is forwards
  218        * and -1 is backwards in time.
  219        * This value does not necessarily reflect the
  220        * true direction of the duration if the duration
  221        * is not normalized or not normalizable.
  222        */
  223       public final int getSign()
  224           { return _sign; }
  225   
  226       /**
  227        * Gets the year component.
  228        */
  229       public final int getYear()
  230           { return _CY;  }
  231   
  232       /**
  233        * Gets the month-of-year component.
  234        */
  235       public final int getMonth()
  236           { return _M;  }
  237   
  238       /**
  239        * Gets the day-of-month component.
  240        */
  241       public final int getDay()
  242           { return _D; }
  243   
  244       /**
  245        * Gets the hour-of-day component.
  246        */
  247       public final int getHour()
  248           { return _h; }
  249   
  250       /**
  251        * Gets the minute-of-hour component.
  252        */
  253       public final int getMinute()
  254           { return _m; }
  255   
  256       /**
  257        * Gets the second-of-minute component.
  258        */
  259       public final int getSecond()
  260           { return _s; }
  261   
  262   
  263       /**
  264        * Gets the fraction-of-second. Range from 0 (inclusive) to 1 (exclusive).
  265        */
  266       public BigDecimal getFraction()
  267           { return _fs; }
  268   
  269       /**
  270        * Returns true if all of the individual components
  271        * of the duration are nonnegative.
  272        */
  273       public boolean isValid()
  274       {
  275           return GDurationBuilder.isValidDuration(this);
  276       }
  277   
  278   
  279       /**
  280        * Normalize a duration value. This ensures that months,
  281        * hours, minutes, seconds, and fractions are positive and
  282        * within the ranges 0..11, 0..23, 0..59, etc. Negative
  283        * durations are indicated by a negative sign rather
  284        * than negative components.
  285        * <p>
  286        * Most duration specifications can be normalized to
  287        * valid durations with all positive components, but
  288        * not all of them can.
  289        * <p>
  290        * The only situations which cannot be normalized are
  291        * where the year/month and the day/hour/minute/second
  292        * offsets are of opposite sign. Days cannot be carried
  293        * into months since the length of a Gregorian month is
  294        * variable depending on when the duration is applied.
  295        * In these cases, this method normalizes the components
  296        * so that "day" is the only negative component.
  297        */
  298       public void normalize()
  299       {
  300           _normalizeImpl(true);
  301       }
  302   
  303       /**
  304        * fQuotient(a, b) = the greatest integer less than or equal to a/b
  305        */
  306       private static final long _fQuotient(long a, int b)
  307       {
  308           if ((a < 0) == (b < 0))
  309               return a / b;
  310   
  311           return -((b - a - 1) / b);
  312       }
  313   
  314       /**
  315        * modulo(a, b) = a - fQuotient(a,b)*b
  316        */
  317       private static final int _mod(long a, int b, long quotient)
  318       {
  319           return (int)(a - quotient*b) ;
  320       }
  321   
  322   
  323       /**
  324        * Private implemenation of normalize. The flag is
  325        * to facilitate this method calling itself without
  326        * danger of infinite recursion.
  327        */
  328       private void _normalizeImpl(boolean adjustSign)
  329       {
  330           long temp;
  331   
  332           // months to years
  333           if (_M < 0 || _M > 11)
  334           {
  335               temp = _M;
  336               long ycarry = _fQuotient(temp, 12);
  337               _M = _mod(temp, 12, ycarry);
  338               _CY += ycarry;
  339           }
  340   
  341           long carry = 0;
  342   
  343           // fractions to seconds
  344           if (_fs != null && (_fs.signum() < 0 || _fs.compareTo(GDate._one) >= 0))
  345           {
  346               BigDecimal bdcarry = _fs.setScale(0, BigDecimal.ROUND_FLOOR);
  347               _fs = _fs.subtract(bdcarry);
  348               carry = bdcarry.intValue();
  349           }
  350   
  351           if (carry != 0 || _s < 0 || _s > 59 || _m < 0 || _m > 50 || _h < 0 || _h > 23)
  352           {
  353               // seconds
  354               temp = _s + carry;
  355               carry = _fQuotient(temp, 60);
  356               _s = _mod(temp, 60, carry);
  357   
  358               // minutes
  359               temp = _m + carry;
  360               carry = _fQuotient(temp, 60);
  361               _m = _mod(temp, 60, carry);
  362   
  363               // hours
  364               temp = _h + carry;
  365               carry = _fQuotient(temp, 24);
  366               _h = _mod(temp, 24, carry);
  367               _D += carry;
  368           }
  369   
  370           if (_CY == 0 && _M == 0 && _D == 0 && _h == 0 && _m == 0 && _s == 0 && (_fs == null || _fs.signum() == 0))
  371               _sign = 1;
  372   
  373           if (adjustSign && (_D < 0 || _CY < 0))
  374           {
  375               int sign = (_D <= 0 && (_CY < 0 || _CY == 0 && _M == 0)) ? -_sign : _getTotalSignSlowly();
  376               if (sign == 2)
  377                   sign = (_CY < 0) ? -_sign : _sign;
  378               if (sign == 0)
  379                   sign = 1;
  380               if (sign != _sign)
  381               {
  382                   _sign = sign;
  383                   _CY = -_CY;
  384                   _M = -_M;
  385                   _D = -_D;
  386                   _h = -_h;
  387                   _m = -_m;
  388                   _s = -_s;
  389                   if (_fs != null)
  390                       _fs = _fs.negate();
  391               }
  392               _normalizeImpl(false);
  393           }
  394       }
  395   
  396   
  397       /* package */ static boolean isValidDuration(GDurationSpecification spec)
  398       {
  399           if (!(spec.getSign() == 1 || spec.getSign() == -1))
  400               return false;
  401   
  402           return (spec.getYear() >= 0 && spec.getMonth() >= 0 && spec.getDay() >= 0 &&
  403                   spec.getHour() >= 0 && spec.getMinute() >= 0  && spec.getSecond() >= 0 &&
  404                   spec.getFraction().signum() >= 0);
  405       }
  406   
  407       /**
  408        * Comparison to another GDuration.
  409        * <ul>
  410        * <li>Returns -1 if this < duration. (less-than)
  411        * <li>Returns 0 if this == duration. (equal)
  412        * <li>Returns 1 if this > duration. (greater-than)
  413        * <li>Returns 2 if this <> duration. (incomparable)
  414        * </ul>
  415        * Two instances are incomparable if they have different amounts
  416        * of information.
  417        */
  418       public final int compareToGDuration(GDurationSpecification duration)
  419       {
  420           return GDurationBuilder.compareDurations(this, duration);
  421       }
  422   
  423       /**
  424        * The natural string representation of the duration.
  425        * <p>
  426        * Any components that are zero are omitted. Note that if the duration
  427        * is invalid, i.e., it has negative components, those negative
  428        * components are serialized out here. To check for validity, use
  429        * the isValid() method; and to normalize most durations to a valid
  430        * form use the normalize() method.
  431        */
  432       public String toString()
  433       {
  434           return GDurationBuilder.formatDuration(this);
  435       }
  436   
  437       /* package */ static int compareDurations(GDurationSpecification d1, GDurationSpecification d2)
  438       {
  439           // first do an all-fields check
  440           if (d1.getFraction().signum() == 0 && d2.getFraction().signum() == 0)
  441           {
  442               int s1 = d1.getSign();
  443               int s2 = d2.getSign();
  444               long month1 = s1 * ((long)d1.getYear() * 12 + d1.getMonth());
  445               long month2 = s2 * ((long)d2.getYear() * 12 + d2.getMonth());
  446               long sec1 = s1 * ((((long)d1.getDay() * 24 + d1.getHour()) * 60 + d1.getMinute()) * 60 + d1.getSecond());
  447               long sec2 = s2 * ((((long)d2.getDay() * 24 + d2.getHour()) * 60 + d2.getMinute()) * 60 + d2.getSecond());
  448               if (month1 == month2)
  449               {
  450                   if (sec1 == sec2)
  451                       return 0;
  452                   if (sec1 < sec2)
  453                       return -1;
  454                   if (sec1 > sec2)
  455                       return 1;
  456               }
  457               if (month1 < month2 && sec1 - sec2 < 28 * 24 * 60 * 60)
  458                   return -1;
  459               if (month1 > month2 && sec2 - sec1 < 28 * 24 * 60 * 60)
  460                   return 1;
  461           }
  462   
  463           // the answer isn't obvious, so then do a total-sign check
  464           GDurationBuilder diff = new GDurationBuilder(d1);
  465           diff.subtractGDuration(d2);
  466           return diff._getTotalSignSlowly();
  467       }
  468   
  469       /**
  470        * Per schema spec, comparison of durations is simply done
  471        * by calculating adding the duration to these four dates and
  472        * comparing the results. If the results are ambiguous, the
  473        * answer is "incomparable".
  474        */
  475       private static final GDate[] _compDate = new GDate[]
  476       {
  477           new GDate(1696, 9, 1, 0, 0, 0, null, 0, 0, 0),
  478           new GDate(1697, 2, 1, 0, 0, 0, null, 0, 0, 0),
  479           new GDate(1903, 3, 1, 0, 0, 0, null, 0, 0, 0),
  480           new GDate(1903, 7, 1, 0, 0, 0, null, 0, 0, 0)
  481       };
  482   
  483   
  484       /**
  485        * This returns the total sign of the duration, +1
  486        * if the duration moves forward in time, -1 if the
  487        * duration moves backwards in time, 0 if the duration
  488        * is zero-length, and 2 if the duration may be positive
  489        * or negative depending on the date.
  490        *
  491        * (For example, one month minus 30 days is indeterminate).
  492        */
  493       private int _getTotalSignSlowly()
  494       {
  495           int pos = 0;
  496           int neg = 0;
  497           int zer = 0;
  498   
  499           GDateBuilder enddate = new GDateBuilder();
  500           for (int i = 0; i < _compDate.length; i++)
  501           {
  502               enddate.setGDate(_compDate[i]);
  503               enddate.addGDuration(this);
  504               switch (enddate.compareToGDate(_compDate[i]))
  505               {
  506                   case -1:
  507                       neg++; break;
  508                   case 0:
  509                       zer++; break;
  510                   case 1:
  511                       pos++; break;
  512               }
  513           }
  514   
  515           if (pos == _compDate.length)
  516               return +1;
  517           if (neg == _compDate.length)
  518               return -1;
  519           if (zer == _compDate.length)
  520               return 0;
  521           return 2;
  522       }
  523   
  524       /* package */ static String formatDuration(GDurationSpecification duration)
  525       {
  526           // Sign+P:      (-)?P
  527           // Year:        (?:(\d+)Y)?
  528           // Month:       (?:(\d+)M)?
  529           // Day:         (?:(\d+)D)?
  530           // Time:        (?:(T)
  531           // Hours:          (?:(\d+)H)?
  532           // Minutes:        (?:(\d+)M)?
  533           // Seconds:        (?:(\d+(?:\.\d*)?|(?:.\d+)S)?
  534   
  535           StringBuffer message = new StringBuffer(30);
  536   
  537           if (duration.getSign() < 0)
  538               message.append('-');
  539   
  540           message.append('P');
  541   
  542           if (duration.getYear() != 0)
  543           {
  544               message.append(duration.getYear());
  545               message.append('Y');
  546           }
  547   
  548           if (duration.getMonth() != 0)
  549           {
  550               message.append(duration.getMonth());
  551               message.append('M');
  552           }
  553   
  554           if (duration.getDay() != 0)
  555           {
  556               message.append(duration.getDay());
  557               message.append('D');
  558           }
  559   
  560           if (duration.getHour() != 0 || duration.getMinute() != 0 || duration.getSecond() != 0 ||
  561                (duration.getFraction().signum() != 0))
  562           {
  563               message.append('T');
  564           }
  565   
  566           if (duration.getHour() != 0)
  567           {
  568               message.append(duration.getHour());
  569               message.append('H');
  570           }
  571   
  572           if (duration.getMinute() != 0)
  573           {
  574               message.append(duration.getMinute());
  575               message.append('M');
  576           }
  577   
  578           if (duration.getFraction().signum() != 0)
  579           {
  580               BigDecimal s = duration.getFraction();
  581               if (duration.getSecond() != 0)
  582                   s = s.add(BigDecimal.valueOf(duration.getSecond()));
  583               // todo when upgrade to 1.5  message.append(s.stripTrailingZeros().toPlainString());
  584               message.append(stripTrailingZeros(toPlainString(s)));
  585               message.append('S');
  586           }
  587           else if (duration.getSecond() != 0)
  588           {
  589               message.append(duration.getSecond());
  590               message.append('S');
  591           }
  592           else if (message.length() <= 2)
  593               // Specify zero seconds if everything was 0
  594               message.append("T0S");
  595   
  596           return message.toString();
  597       }
  598   
  599       public static String toPlainString(BigDecimal bd)
  600       {
  601           BigInteger intVal = bd.unscaledValue();
  602           int scale = bd.scale();
  603           String intValStr = intVal.toString();
  604           if (scale == 0)
  605               return intValStr;
  606   
  607           boolean isNegative = (intValStr.charAt(0) == '-');
  608   
  609           int point = intValStr.length() - scale - (isNegative ? 1 : 0);
  610   
  611           StringBuffer sb = new StringBuffer(intValStr.length() + 2 + (point <= 0 ? (-point + 1) : 0));
  612           if (point <= 0)
  613           {
  614               // prepend zeros and a decimal point.
  615               if (isNegative) sb.append('-');
  616               sb.append('0').append('.');
  617               while (point < 0)
  618               {
  619                   sb.append('0');
  620                   point++;
  621               }
  622               sb.append(intValStr.substring(isNegative ? 1 : 0));
  623           }
  624           else if (point < intValStr.length())
  625           {
  626               // No zeros needed
  627               sb.append(intValStr);
  628               sb.insert(point + (isNegative ? 1 : 0), '.');
  629           }
  630           else
  631           {
  632               // append zeros if not 0
  633               sb.append(intValStr);
  634               if (!intVal.equals(BigInteger.ZERO))
  635                   for (int i = intValStr.length(); i < point; i++)
  636                       sb.append('0');
  637           }
  638           return sb.toString();
  639       }
  640   
  641       public static String stripTrailingZeros(String s)
  642       {
  643           boolean seenDot = false;
  644           int i = s.length() - 1;
  645           int zeroIndex = i;
  646   
  647           while(i>=0)
  648           {
  649               if (s.charAt(i)!='0')
  650                   break;
  651               i--;
  652               zeroIndex--;
  653           }
  654           while(i>=0)
  655           {
  656               if (s.charAt(i)=='E')
  657                   return s;
  658               if (s.charAt(i)=='.')
  659               {
  660                   seenDot = true;
  661                   break;
  662               }
  663               i--;
  664           }
  665           
  666           return seenDot? s.substring(0, zeroIndex+1) : s;
  667       }
  668   }

Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]