View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math.fraction;
19  
20  import java.io.Serializable;
21  import java.math.BigInteger;
22  import java.text.FieldPosition;
23  import java.text.NumberFormat;
24  import java.text.ParseException;
25  import java.text.ParsePosition;
26  import java.util.Locale;
27  
28  import org.apache.commons.math.MathRuntimeException;
29  
30  /**
31   * Formats a BigFraction number in proper format or improper format.
32   * <p>
33   * The number format for each of the whole number, numerator and,
34   * denominator can be configured.
35   * </p>
36   *
37   * @since 2.0
38   * @version $Revision: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $
39   */
40  public class BigFractionFormat extends AbstractFormat implements Serializable {
41      
42      /** Serializable version identifier */
43      private static final long serialVersionUID = -2932167925527338976L;
44  
45      /**
46       * Create an improper formatting instance with the default number format
47       * for the numerator and denominator.  
48       */
49      public BigFractionFormat() {
50      }
51  
52      /**
53       * Create an improper formatting instance with a custom number format for
54       * both the numerator and denominator.
55       * @param format the custom format for both the numerator and denominator.
56       */
57      public BigFractionFormat(final NumberFormat format) {
58          super(format);
59      }
60  
61      /**
62       * Create an improper formatting instance with a custom number format for
63       * the numerator and a custom number format for the denominator.
64       * @param numeratorFormat the custom format for the numerator.
65       * @param denominatorFormat the custom format for the denominator.
66       */
67      public BigFractionFormat(final NumberFormat numeratorFormat,
68                               final NumberFormat denominatorFormat) {
69          super(numeratorFormat, denominatorFormat);
70      }
71  
72      /**
73       * Get the set of locales for which complex formats are available.  This
74       * is the same set as the {@link NumberFormat} set. 
75       * @return available complex format locales.
76       */
77      public static Locale[] getAvailableLocales() {
78          return NumberFormat.getAvailableLocales();
79      }
80  
81      /**
82       * This static method calls formatBigFraction() on a default instance of
83       * BigFractionFormat.
84       *
85       * @param f BigFraction object to format
86       * @return A formatted BigFraction in proper form.
87       */
88      public static String formatBigFraction(final BigFraction f) {
89          return getImproperInstance().format(f);
90      }
91      
92      /**
93       * Returns the default complex format for the current locale.
94       * @return the default complex format.
95       */
96      public static BigFractionFormat getImproperInstance() {
97          return getImproperInstance(Locale.getDefault());
98      }
99      
100     /**
101      * Returns the default complex format for the given locale.
102      * @param locale the specific locale used by the format.
103      * @return the complex format specific to the given locale.
104      */
105     public static BigFractionFormat getImproperInstance(final Locale locale) {
106         return new BigFractionFormat(getDefaultNumberFormat(locale));
107     }
108     
109     /**
110      * Returns the default complex format for the current locale.
111      * @return the default complex format.
112      */
113     public static BigFractionFormat getProperInstance() {
114         return getProperInstance(Locale.getDefault());
115     }
116     
117     /**
118      * Returns the default complex format for the given locale.
119      * @param locale the specific locale used by the format.
120      * @return the complex format specific to the given locale.
121      */
122     public static BigFractionFormat getProperInstance(final Locale locale) {
123         return new ProperBigFractionFormat(getDefaultNumberFormat(locale));
124     }
125     
126     /**
127      * Formats a {@link BigFraction} object to produce a string.  The BigFraction is
128      * output in improper format.
129      *
130      * @param BigFraction the object to format.
131      * @param toAppendTo where the text is to be appended
132      * @param pos On input: an alignment field, if desired. On output: the
133      *            offsets of the alignment field
134      * @return the value passed in as toAppendTo.
135      */
136     public StringBuffer format(final BigFraction BigFraction,
137                                final StringBuffer toAppendTo, final FieldPosition pos) {
138         
139         pos.setBeginIndex(0);
140         pos.setEndIndex(0);
141 
142         getNumeratorFormat().format(BigFraction.getNumerator(), toAppendTo, pos);
143         toAppendTo.append(" / ");
144         getDenominatorFormat().format(BigFraction.getDenominator(), toAppendTo, pos);
145         
146         return toAppendTo;
147     }
148     
149     /**
150      * Formats an object and appends the result to a StringBuffer.
151      * <code>obj</code> must be either a  {@link BigFraction} object or a
152      * {@link BigInteger} object or a {@link Number} object. Any other type of
153      * object will result in an {@link IllegalArgumentException} being thrown.
154      *
155      * @param obj the object to format.
156      * @param toAppendTo where the text is to be appended
157      * @param pos On input: an alignment field, if desired. On output: the
158      *            offsets of the alignment field
159      * @return the value passed in as toAppendTo.
160      * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
161      * @throws IllegalArgumentException is <code>obj</code> is not a valid type.
162      */
163     @Override
164     public StringBuffer format(final Object obj,
165                                final StringBuffer toAppendTo, final FieldPosition pos) {
166 
167         final StringBuffer ret;
168         if (obj instanceof BigFraction) {
169             ret = format((BigFraction) obj, toAppendTo, pos);
170         } else if (obj instanceof BigInteger) {
171             ret = format(new BigFraction((BigInteger) obj), toAppendTo, pos);
172         } else if (obj instanceof Number) {
173             ret = format(new BigFraction(((Number) obj).doubleValue()),
174                          toAppendTo, pos);
175         } else { 
176             throw MathRuntimeException.createIllegalArgumentException(
177                 "cannot format given object as a fraction number");
178         }
179         
180         return ret;
181     }
182 
183     /**
184      * Parses a string to produce a {@link BigFraction} object.
185      * @param source the string to parse
186      * @return the parsed {@link BigFraction} object.
187      * @exception ParseException if the beginning of the specified string
188      *            cannot be parsed.
189      */
190     @Override
191     public BigFraction parse(final String source) throws ParseException {
192         final ParsePosition parsePosition = new ParsePosition(0);
193         final BigFraction result = parse(source, parsePosition);
194         if (parsePosition.getIndex() == 0) {
195             throw MathRuntimeException.createParseException(
196                     parsePosition.getErrorIndex(),
197                     "unparseable fraction number: \"{0}\"", source);
198         }
199         return result;
200     }
201     
202     /**
203      * Parses a string to produce a {@link BigFraction} object.
204      * This method expects the string to be formatted as an improper BigFraction.  
205      * @param source the string to parse
206      * @param pos input/ouput parsing parameter.
207      * @return the parsed {@link BigFraction} object.
208      */
209     @Override
210     public BigFraction parse(final String source, final ParsePosition pos) {
211         final int initialIndex = pos.getIndex();
212 
213         // parse whitespace
214         parseAndIgnoreWhitespace(source, pos);
215 
216         // parse numerator
217         final BigInteger num = parseNextBigInteger(source, pos);
218         if (num == null) {
219             // invalid integer number
220             // set index back to initial, error index should already be set
221             // character examined.
222             pos.setIndex(initialIndex);
223             return null;
224         }
225 
226         // parse '/'
227         final int startIndex = pos.getIndex();
228         final char c = parseNextCharacter(source, pos);
229         switch (c) {
230         case 0 :
231             // no '/'
232             // return num as a BigFraction
233             return new BigFraction(num);
234         case '/' :
235             // found '/', continue parsing denominator
236             break;
237         default :
238             // invalid '/'
239             // set index back to initial, error index should be the last
240             // character examined.
241             pos.setIndex(initialIndex);
242             pos.setErrorIndex(startIndex);
243             return null;
244         }
245 
246         // parse whitespace
247         parseAndIgnoreWhitespace(source, pos);
248 
249         // parse denominator
250         final BigInteger den = parseNextBigInteger(source, pos);
251         if (den == null) {
252             // invalid integer number
253             // set index back to initial, error index should already be set
254             // character examined.
255             pos.setIndex(initialIndex);
256             return null;
257         }
258 
259         return new BigFraction(num, den);
260     }
261 
262     /**
263      * Parses a string to produce a <code>BigInteger</code>.
264      * @param source the string to parse
265      * @param pos input/ouput parsing parameter.
266      * @return a parsed <code>BigInteger</code> or null if string does not
267      * contain a BigInteger at the specified position
268      */
269     protected BigInteger parseNextBigInteger(final String source,
270                                              final ParsePosition pos) {
271 
272         final int start = pos.getIndex();
273          int end = (source.charAt(start) == '-') ? (start + 1) : start;
274          while((end < source.length()) &&
275                Character.isDigit(source.charAt(end))) {
276              ++end;
277          }
278 
279          try {
280              BigInteger n = new BigInteger(source.substring(start, end));
281              pos.setIndex(end);
282              return n;
283          } catch (NumberFormatException nfe) {
284              pos.setErrorIndex(start);
285              return null;
286          }
287 
288     }
289 
290 }