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 }