001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.math.fraction;
019
020 import java.io.Serializable;
021 import java.math.BigInteger;
022 import java.text.FieldPosition;
023 import java.text.NumberFormat;
024 import java.text.ParseException;
025 import java.text.ParsePosition;
026 import java.util.Locale;
027
028 import org.apache.commons.math.MathRuntimeException;
029
030 /**
031 * Formats a BigFraction number in proper format or improper format.
032 * <p>
033 * The number format for each of the whole number, numerator and,
034 * denominator can be configured.
035 * </p>
036 *
037 * @since 2.0
038 * @version $Revision: 811685 $ $Date: 2009-09-05 13:36:48 -0400 (Sat, 05 Sep 2009) $
039 */
040 public class BigFractionFormat extends AbstractFormat implements Serializable {
041
042 /** Serializable version identifier */
043 private static final long serialVersionUID = -2932167925527338976L;
044
045 /**
046 * Create an improper formatting instance with the default number format
047 * for the numerator and denominator.
048 */
049 public BigFractionFormat() {
050 }
051
052 /**
053 * Create an improper formatting instance with a custom number format for
054 * both the numerator and denominator.
055 * @param format the custom format for both the numerator and denominator.
056 */
057 public BigFractionFormat(final NumberFormat format) {
058 super(format);
059 }
060
061 /**
062 * Create an improper formatting instance with a custom number format for
063 * the numerator and a custom number format for the denominator.
064 * @param numeratorFormat the custom format for the numerator.
065 * @param denominatorFormat the custom format for the denominator.
066 */
067 public BigFractionFormat(final NumberFormat numeratorFormat,
068 final NumberFormat denominatorFormat) {
069 super(numeratorFormat, denominatorFormat);
070 }
071
072 /**
073 * Get the set of locales for which complex formats are available. This
074 * is the same set as the {@link NumberFormat} set.
075 * @return available complex format locales.
076 */
077 public static Locale[] getAvailableLocales() {
078 return NumberFormat.getAvailableLocales();
079 }
080
081 /**
082 * This static method calls formatBigFraction() on a default instance of
083 * BigFractionFormat.
084 *
085 * @param f BigFraction object to format
086 * @return A formatted BigFraction in proper form.
087 */
088 public static String formatBigFraction(final BigFraction f) {
089 return getImproperInstance().format(f);
090 }
091
092 /**
093 * Returns the default complex format for the current locale.
094 * @return the default complex format.
095 */
096 public static BigFractionFormat getImproperInstance() {
097 return getImproperInstance(Locale.getDefault());
098 }
099
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 }