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.math3.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.ParsePosition;
025 import java.util.Locale;
026
027 import org.apache.commons.math3.exception.MathIllegalArgumentException;
028 import org.apache.commons.math3.exception.MathParseException;
029 import org.apache.commons.math3.exception.util.LocalizedFormats;
030
031 /**
032 * Formats a BigFraction number in proper format or improper format.
033 * <p>
034 * The number format for each of the whole number, numerator and,
035 * denominator can be configured.
036 * </p>
037 *
038 * @since 2.0
039 * @version $Id: BigFractionFormat.java 1416643 2012-12-03 19:37:14Z tn $
040 */
041 public class BigFractionFormat extends AbstractFormat implements Serializable {
042
043 /** Serializable version identifier */
044 private static final long serialVersionUID = -2932167925527338976L;
045
046 /**
047 * Create an improper formatting instance with the default number format
048 * for the numerator and denominator.
049 */
050 public BigFractionFormat() {
051 }
052
053 /**
054 * Create an improper formatting instance with a custom number format for
055 * both the numerator and denominator.
056 * @param format the custom format for both the numerator and denominator.
057 */
058 public BigFractionFormat(final NumberFormat format) {
059 super(format);
060 }
061
062 /**
063 * Create an improper formatting instance with a custom number format for
064 * the numerator and a custom number format for the denominator.
065 * @param numeratorFormat the custom format for the numerator.
066 * @param denominatorFormat the custom format for the denominator.
067 */
068 public BigFractionFormat(final NumberFormat numeratorFormat,
069 final NumberFormat denominatorFormat) {
070 super(numeratorFormat, denominatorFormat);
071 }
072
073 /**
074 * Get the set of locales for which complex formats are available. This
075 * is the same set as the {@link NumberFormat} set.
076 * @return available complex format locales.
077 */
078 public static Locale[] getAvailableLocales() {
079 return NumberFormat.getAvailableLocales();
080 }
081
082 /**
083 * This static method calls formatBigFraction() on a default instance of
084 * BigFractionFormat.
085 *
086 * @param f BigFraction object to format
087 * @return A formatted BigFraction in proper form.
088 */
089 public static String formatBigFraction(final BigFraction f) {
090 return getImproperInstance().format(f);
091 }
092
093 /**
094 * Returns the default complex format for the current locale.
095 * @return the default complex format.
096 */
097 public static BigFractionFormat getImproperInstance() {
098 return getImproperInstance(Locale.getDefault());
099 }
100
101 /**
102 * Returns the default complex format for the given locale.
103 * @param locale the specific locale used by the format.
104 * @return the complex format specific to the given locale.
105 */
106 public static BigFractionFormat getImproperInstance(final Locale locale) {
107 return new BigFractionFormat(getDefaultNumberFormat(locale));
108 }
109
110 /**
111 * Returns the default complex format for the current locale.
112 * @return the default complex format.
113 */
114 public static BigFractionFormat getProperInstance() {
115 return getProperInstance(Locale.getDefault());
116 }
117
118 /**
119 * Returns the default complex format for the given locale.
120 * @param locale the specific locale used by the format.
121 * @return the complex format specific to the given locale.
122 */
123 public static BigFractionFormat getProperInstance(final Locale locale) {
124 return new ProperBigFractionFormat(getDefaultNumberFormat(locale));
125 }
126
127 /**
128 * Formats a {@link BigFraction} object to produce a string. The BigFraction is
129 * output in improper format.
130 *
131 * @param BigFraction the object to format.
132 * @param toAppendTo where the text is to be appended
133 * @param pos On input: an alignment field, if desired. On output: the
134 * offsets of the alignment field
135 * @return the value passed in as toAppendTo.
136 */
137 public StringBuffer format(final BigFraction BigFraction,
138 final StringBuffer toAppendTo, final FieldPosition pos) {
139
140 pos.setBeginIndex(0);
141 pos.setEndIndex(0);
142
143 getNumeratorFormat().format(BigFraction.getNumerator(), toAppendTo, pos);
144 toAppendTo.append(" / ");
145 getDenominatorFormat().format(BigFraction.getDenominator(), toAppendTo, pos);
146
147 return toAppendTo;
148 }
149
150 /**
151 * Formats an object and appends the result to a StringBuffer.
152 * <code>obj</code> must be either a {@link BigFraction} object or a
153 * {@link BigInteger} object or a {@link Number} object. Any other type of
154 * object will result in an {@link IllegalArgumentException} being thrown.
155 *
156 * @param obj the object to format.
157 * @param toAppendTo where the text is to be appended
158 * @param pos On input: an alignment field, if desired. On output: the
159 * offsets of the alignment field
160 * @return the value passed in as toAppendTo.
161 * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
162 * @throws MathIllegalArgumentException if <code>obj</code> is not a valid type.
163 */
164 @Override
165 public StringBuffer format(final Object obj,
166 final StringBuffer toAppendTo, final FieldPosition pos) {
167
168 final StringBuffer ret;
169 if (obj instanceof BigFraction) {
170 ret = format((BigFraction) obj, toAppendTo, pos);
171 } else if (obj instanceof BigInteger) {
172 ret = format(new BigFraction((BigInteger) obj), toAppendTo, pos);
173 } else if (obj instanceof Number) {
174 ret = format(new BigFraction(((Number) obj).doubleValue()),
175 toAppendTo, pos);
176 } else {
177 throw new MathIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION);
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 MathParseException if the beginning of the specified string
188 * cannot be parsed.
189 */
190 @Override
191 public BigFraction parse(final String source) throws MathParseException {
192 final ParsePosition parsePosition = new ParsePosition(0);
193 final BigFraction result = parse(source, parsePosition);
194 if (parsePosition.getIndex() == 0) {
195 throw new MathParseException(source, parsePosition.getErrorIndex(), BigFraction.class);
196 }
197 return result;
198 }
199
200 /**
201 * Parses a string to produce a {@link BigFraction} object.
202 * This method expects the string to be formatted as an improper BigFraction.
203 * @param source the string to parse
204 * @param pos input/output parsing parameter.
205 * @return the parsed {@link BigFraction} object.
206 */
207 @Override
208 public BigFraction parse(final String source, final ParsePosition pos) {
209 final int initialIndex = pos.getIndex();
210
211 // parse whitespace
212 parseAndIgnoreWhitespace(source, pos);
213
214 // parse numerator
215 final BigInteger num = parseNextBigInteger(source, pos);
216 if (num == null) {
217 // invalid integer number
218 // set index back to initial, error index should already be set
219 // character examined.
220 pos.setIndex(initialIndex);
221 return null;
222 }
223
224 // parse '/'
225 final int startIndex = pos.getIndex();
226 final char c = parseNextCharacter(source, pos);
227 switch (c) {
228 case 0 :
229 // no '/'
230 // return num as a BigFraction
231 return new BigFraction(num);
232 case '/' :
233 // found '/', continue parsing denominator
234 break;
235 default :
236 // invalid '/'
237 // set index back to initial, error index should be the last
238 // character examined.
239 pos.setIndex(initialIndex);
240 pos.setErrorIndex(startIndex);
241 return null;
242 }
243
244 // parse whitespace
245 parseAndIgnoreWhitespace(source, pos);
246
247 // parse denominator
248 final BigInteger den = parseNextBigInteger(source, pos);
249 if (den == null) {
250 // invalid integer number
251 // set index back to initial, error index should already be set
252 // character examined.
253 pos.setIndex(initialIndex);
254 return null;
255 }
256
257 return new BigFraction(num, den);
258 }
259
260 /**
261 * Parses a string to produce a <code>BigInteger</code>.
262 * @param source the string to parse
263 * @param pos input/output parsing parameter.
264 * @return a parsed <code>BigInteger</code> or null if string does not
265 * contain a BigInteger at the specified position
266 */
267 protected BigInteger parseNextBigInteger(final String source,
268 final ParsePosition pos) {
269
270 final int start = pos.getIndex();
271 int end = (source.charAt(start) == '-') ? (start + 1) : start;
272 while((end < source.length()) &&
273 Character.isDigit(source.charAt(end))) {
274 ++end;
275 }
276
277 try {
278 BigInteger n = new BigInteger(source.substring(start, end));
279 pos.setIndex(end);
280 return n;
281 } catch (NumberFormatException nfe) {
282 pos.setErrorIndex(start);
283 return null;
284 }
285
286 }
287
288 }