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.text.FieldPosition; 21 import java.text.NumberFormat; 22 import java.text.ParseException; 23 import java.text.ParsePosition; 24 import java.util.Locale; 25 26 import org.apache.commons.math.ConvergenceException; 27 import org.apache.commons.math.MathRuntimeException; 28 import org.apache.commons.math.exception.util.LocalizedFormats; 29 30 /** 31 * Formats a Fraction number in proper format or improper format. The number 32 * format for each of the whole number, numerator and, denominator can be 33 * configured. 34 * 35 * @since 1.1 36 * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 aot 2010) $ 37 */ 38 public class FractionFormat extends AbstractFormat { 39 40 /** Serializable version identifier */ 41 private static final long serialVersionUID = 3008655719530972611L; 42 43 /** 44 * Create an improper formatting instance with the default number format 45 * for the numerator and denominator. 46 */ 47 public FractionFormat() { 48 } 49 50 /** 51 * Create an improper formatting instance with a custom number format for 52 * both the numerator and denominator. 53 * @param format the custom format for both the numerator and denominator. 54 */ 55 public FractionFormat(final NumberFormat format) { 56 super(format); 57 } 58 59 /** 60 * Create an improper formatting instance with a custom number format for 61 * the numerator and a custom number format for the denominator. 62 * @param numeratorFormat the custom format for the numerator. 63 * @param denominatorFormat the custom format for the denominator. 64 */ 65 public FractionFormat(final NumberFormat numeratorFormat, 66 final NumberFormat denominatorFormat) { 67 super(numeratorFormat, denominatorFormat); 68 } 69 70 /** 71 * Get the set of locales for which complex formats are available. This 72 * is the same set as the {@link NumberFormat} set. 73 * @return available complex format locales. 74 */ 75 public static Locale[] getAvailableLocales() { 76 return NumberFormat.getAvailableLocales(); 77 } 78 79 /** 80 * This static method calls formatFraction() on a default instance of 81 * FractionFormat. 82 * 83 * @param f Fraction object to format 84 * @return A formatted fraction in proper form. 85 */ 86 public static String formatFraction(Fraction f) { 87 return getImproperInstance().format(f); 88 } 89 90 /** 91 * Returns the default complex format for the current locale. 92 * @return the default complex format. 93 */ 94 public static FractionFormat getImproperInstance() { 95 return getImproperInstance(Locale.getDefault()); 96 } 97 98 /** 99 * Returns the default complex format for the given locale. 100 * @param locale the specific locale used by the format. 101 * @return the complex format specific to the given locale. 102 */ 103 public static FractionFormat getImproperInstance(final Locale locale) { 104 return new FractionFormat(getDefaultNumberFormat(locale)); 105 } 106 107 /** 108 * Returns the default complex format for the current locale. 109 * @return the default complex format. 110 */ 111 public static FractionFormat getProperInstance() { 112 return getProperInstance(Locale.getDefault()); 113 } 114 115 /** 116 * Returns the default complex format for the given locale. 117 * @param locale the specific locale used by the format. 118 * @return the complex format specific to the given locale. 119 */ 120 public static FractionFormat getProperInstance(final Locale locale) { 121 return new ProperFractionFormat(getDefaultNumberFormat(locale)); 122 } 123 124 /** 125 * Create a default number format. The default number format is based on 126 * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only 127 * customizing is the maximum number of fraction digits, which is set to 0. 128 * @return the default number format. 129 */ 130 protected static NumberFormat getDefaultNumberFormat() { 131 return getDefaultNumberFormat(Locale.getDefault()); 132 } 133 134 /** 135 * Formats a {@link Fraction} object to produce a string. The fraction is 136 * output in improper format. 137 * 138 * @param fraction the object to format. 139 * @param toAppendTo where the text is to be appended 140 * @param pos On input: an alignment field, if desired. On output: the 141 * offsets of the alignment field 142 * @return the value passed in as toAppendTo. 143 */ 144 public StringBuffer format(final Fraction fraction, 145 final StringBuffer toAppendTo, final FieldPosition pos) { 146 147 pos.setBeginIndex(0); 148 pos.setEndIndex(0); 149 150 getNumeratorFormat().format(fraction.getNumerator(), toAppendTo, pos); 151 toAppendTo.append(" / "); 152 getDenominatorFormat().format(fraction.getDenominator(), toAppendTo, 153 pos); 154 155 return toAppendTo; 156 } 157 158 /** 159 * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a 160 * {@link Fraction} object or a {@link Number} object. Any other type of 161 * object will result in an {@link IllegalArgumentException} being thrown. 162 * 163 * @param obj the object to format. 164 * @param toAppendTo where the text is to be appended 165 * @param pos On input: an alignment field, if desired. On output: the 166 * offsets of the alignment field 167 * @return the value passed in as toAppendTo. 168 * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) 169 * @throws IllegalArgumentException is <code>obj</code> is not a valid type. 170 */ 171 @Override 172 public StringBuffer format(final Object obj, 173 final StringBuffer toAppendTo, final FieldPosition pos) { 174 StringBuffer ret = null; 175 176 if (obj instanceof Fraction) { 177 ret = format((Fraction) obj, toAppendTo, pos); 178 } else if (obj instanceof Number) { 179 try { 180 ret = format(new Fraction(((Number) obj).doubleValue()), 181 toAppendTo, pos); 182 } catch (ConvergenceException ex) { 183 throw MathRuntimeException.createIllegalArgumentException( 184 LocalizedFormats.CANNOT_CONVERT_OBJECT_TO_FRACTION, 185 ex.getLocalizedMessage()); 186 } 187 } else { 188 throw MathRuntimeException.createIllegalArgumentException( 189 LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION); 190 } 191 192 return ret; 193 } 194 195 /** 196 * Parses a string to produce a {@link Fraction} object. 197 * @param source the string to parse 198 * @return the parsed {@link Fraction} object. 199 * @exception ParseException if the beginning of the specified string 200 * cannot be parsed. 201 */ 202 @Override 203 public Fraction parse(final String source) throws ParseException { 204 final ParsePosition parsePosition = new ParsePosition(0); 205 final Fraction result = parse(source, parsePosition); 206 if (parsePosition.getIndex() == 0) { 207 throw MathRuntimeException.createParseException( 208 parsePosition.getErrorIndex(), 209 LocalizedFormats.UNPARSEABLE_FRACTION_NUMBER, source); 210 } 211 return result; 212 } 213 214 /** 215 * Parses a string to produce a {@link Fraction} object. This method 216 * expects the string to be formatted as an improper fraction. 217 * @param source the string to parse 218 * @param pos input/ouput parsing parameter. 219 * @return the parsed {@link Fraction} object. 220 */ 221 @Override 222 public Fraction parse(final String source, final ParsePosition pos) { 223 final int initialIndex = pos.getIndex(); 224 225 // parse whitespace 226 parseAndIgnoreWhitespace(source, pos); 227 228 // parse numerator 229 final Number num = getNumeratorFormat().parse(source, pos); 230 if (num == null) { 231 // invalid integer number 232 // set index back to initial, error index should already be set 233 // character examined. 234 pos.setIndex(initialIndex); 235 return null; 236 } 237 238 // parse '/' 239 final int startIndex = pos.getIndex(); 240 final char c = parseNextCharacter(source, pos); 241 switch (c) { 242 case 0 : 243 // no '/' 244 // return num as a fraction 245 return new Fraction(num.intValue(), 1); 246 case '/' : 247 // found '/', continue parsing denominator 248 break; 249 default : 250 // invalid '/' 251 // set index back to initial, error index should be the last 252 // character examined. 253 pos.setIndex(initialIndex); 254 pos.setErrorIndex(startIndex); 255 return null; 256 } 257 258 // parse whitespace 259 parseAndIgnoreWhitespace(source, pos); 260 261 // parse denominator 262 final Number den = getDenominatorFormat().parse(source, pos); 263 if (den == null) { 264 // invalid integer number 265 // set index back to initial, error index should already be set 266 // character examined. 267 pos.setIndex(initialIndex); 268 return null; 269 } 270 271 return new Fraction(num.intValue(), den.intValue()); 272 } 273 274 } 275