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 }