View Javadoc

    1   /*
    2    * Copyright 2005 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.math.fraction;
    17
    18  import java.text.FieldPosition;
    19  import java.text.NumberFormat;
    20  import java.text.ParsePosition;
    21
    22  import org.apache.commons.math.util.MathUtils;
    23
    24  /***
    25   * Formats a Fraction number in proper format.  The number format for each of
    26   * the whole number, numerator and, denominator can be configured.
    27   *
    28   * @since 1.1
    29   * @version $Revision: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $
    30   */
    31  public class ProperFractionFormat extends FractionFormat {
    32
    33      /*** Serializable version identifier */
    34      private static final long serialVersionUID = -6337346779577272307L;
    35
    36      /*** The format used for the whole number. */
    37      private NumberFormat wholeFormat;
    38
    39      /***
    40       * Create a proper formatting instance with the default number format for
    41       * the whole, numerator, and denominator.
    42       */
    43      public ProperFractionFormat() {
    44          this(getDefaultNumberFormat());
    45      }
    46
    47      /***
    48       * Create a proper formatting instance with a custom number format for the
    49       * whole, numerator, and denominator.
    50       * @param format the custom format for the whole, numerator, and
    51       *        denominator.
    52       */
    53      public ProperFractionFormat(NumberFormat format) {
    54          this(format, (NumberFormat)format.clone(), (NumberFormat)format.clone());
    55      }
    56
    57      /***
    58       * Create a proper formatting instance with a custom number format for each
    59       * of the whole, numerator, and denominator.
    60       * @param wholeFormat the custom format for the whole.
    61       * @param numeratorFormat the custom format for the numerator.
    62       * @param denominatorFormat the custom format for the denominator.
    63       */
    64      public ProperFractionFormat(NumberFormat wholeFormat,
    65              NumberFormat numeratorFormat,
    66              NumberFormat denominatorFormat)
    67      {
    68          super(numeratorFormat, denominatorFormat);
    69          setWholeFormat(wholeFormat);
    70      }
    71
    72      /***
    73       * Formats a {@link Fraction} object to produce a string.  The fraction
    74       * is output in proper format.
    75       *
    76       * @param fraction the object to format.
    77       * @param toAppendTo where the text is to be appended
    78       * @param pos On input: an alignment field, if desired. On output: the
    79       *            offsets of the alignment field
    80       * @return the value passed in as toAppendTo.
    81       */
    82      public StringBuffer format(Fraction fraction, StringBuffer toAppendTo,
    83              FieldPosition pos) {
    84
    85          pos.setBeginIndex(0);
    86          pos.setEndIndex(0);
    87
    88          int num = fraction.getNumerator();
    89          int den = fraction.getDenominator();
    90          int whole = num / den;
    91          num = num % den;
    92
    93          if (whole != 0) {
    94              getWholeFormat().format(whole, toAppendTo, pos);
    95              toAppendTo.append(' ');
    96              num = Math.abs(num);
    97          }
    98          getNumeratorFormat().format(num, toAppendTo, pos);
    99          toAppendTo.append(" / ");
    100         getDenominatorFormat().format(den, toAppendTo,
    101             pos);
    102
    103         return toAppendTo;
    104     }
    105
    106     /***
    107      * Access the whole format.
    108      * @return the whole format.
    109      */
    110     public NumberFormat getWholeFormat() {
    111         return wholeFormat;
    112     }
    113
    114     /***
    115      * Parses a string to produce a {@link Fraction} object.  This method
    116      * expects the string to be formatted as a proper fraction.
    117      * @param source the string to parse
    118      * @param pos input/ouput parsing parameter.
    119      * @return the parsed {@link Fraction} object.
    120      */
    121     public Fraction parse(String source, ParsePosition pos) {
    122         // try to parse improper fraction
    123         Fraction ret = super.parse(source, pos);
    124         if (ret != null) {
    125             return ret;
    126         }
    127
    128         int initialIndex = pos.getIndex();
    129
    130         // parse whitespace
    131         parseAndIgnoreWhitespace(source, pos);
    132
    133         // parse whole
    134         Number whole = getWholeFormat().parse(source, pos);
    135         if (whole == null) {
    136             // invalid integer number
    137             // set index back to initial, error index should already be set
    138             // character examined.
    139             pos.setIndex(initialIndex);
    140             return null;
    141         }
    142
    143         // parse whitespace
    144         parseAndIgnoreWhitespace(source, pos);
    145
    146         // parse numerator
    147         Number num = getNumeratorFormat().parse(source, pos);
    148         if (num == null) {
    149             // invalid integer number
    150             // set index back to initial, error index should already be set
    151             // character examined.
    152             pos.setIndex(initialIndex);
    153             return null;
    154         }
    155
    156         // parse '/'
    157         int startIndex = pos.getIndex();
    158         char c = parseNextCharacter(source, pos);
    159         switch (c) {
    160         case 0 :
    161             // no '/'
    162             // return num as a fraction
    163             return new Fraction(num.intValue(), 1);
    164         case '/' :
    165             // found '/', continue parsing denominator
    166             break;
    167         default :
    168             // invalid '/'
    169             // set index back to initial, error index should be the last
    170             // character examined.
    171             pos.setIndex(initialIndex);
    172             pos.setErrorIndex(startIndex);
    173             return null;
    174         }
    175
    176         // parse whitespace
    177         parseAndIgnoreWhitespace(source, pos);
    178
    179         // parse denominator
    180         Number den = getDenominatorFormat().parse(source, pos);
    181         if (den == null) {
    182             // invalid integer number
    183             // set index back to initial, error index should already be set
    184             // character examined.
    185             pos.setIndex(initialIndex);
    186             return null;
    187         }
    188
    189         int w = whole.intValue();
    190         int n = num.intValue();
    191         int d = den.intValue();
    192         return new Fraction(((Math.abs(w) * d) + n) * MathUtils.sign(w), d);
    193     }
    194
    195     /***
    196      * Modify the whole format.
    197      * @param format The new whole format value.
    198      * @throws IllegalArgumentException if <code>format</code> is
    199      *         <code>null</code>.
    200      */
    201     public void setWholeFormat(NumberFormat format) {
    202         if (format == null) {
    203             throw new IllegalArgumentException(
    204                 "whole format can not be null.");
    205         }
    206         this.wholeFormat = format;
    207     }
    208 }