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.text.FieldPosition; 021 import java.text.NumberFormat; 022 import java.text.ParseException; 023 import java.text.ParsePosition; 024 import java.util.Locale; 025 026 import org.apache.commons.math.ConvergenceException; 027 import org.apache.commons.math.MathRuntimeException; 028 import org.apache.commons.math.exception.util.LocalizedFormats; 029 030 /** 031 * Formats a Fraction number in proper format or improper format. The number 032 * format for each of the whole number, numerator and, denominator can be 033 * configured. 034 * 035 * @since 1.1 036 * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 ao??t 2010) $ 037 */ 038 public class FractionFormat extends AbstractFormat { 039 040 /** Serializable version identifier */ 041 private static final long serialVersionUID = 3008655719530972611L; 042 043 /** 044 * Create an improper formatting instance with the default number format 045 * for the numerator and denominator. 046 */ 047 public FractionFormat() { 048 } 049 050 /** 051 * Create an improper formatting instance with a custom number format for 052 * both the numerator and denominator. 053 * @param format the custom format for both the numerator and denominator. 054 */ 055 public FractionFormat(final NumberFormat format) { 056 super(format); 057 } 058 059 /** 060 * Create an improper formatting instance with a custom number format for 061 * the numerator and a custom number format for the denominator. 062 * @param numeratorFormat the custom format for the numerator. 063 * @param denominatorFormat the custom format for the denominator. 064 */ 065 public FractionFormat(final NumberFormat numeratorFormat, 066 final NumberFormat denominatorFormat) { 067 super(numeratorFormat, denominatorFormat); 068 } 069 070 /** 071 * Get the set of locales for which complex formats are available. This 072 * is the same set as the {@link NumberFormat} set. 073 * @return available complex format locales. 074 */ 075 public static Locale[] getAvailableLocales() { 076 return NumberFormat.getAvailableLocales(); 077 } 078 079 /** 080 * This static method calls formatFraction() on a default instance of 081 * FractionFormat. 082 * 083 * @param f Fraction object to format 084 * @return A formatted fraction in proper form. 085 */ 086 public static String formatFraction(Fraction f) { 087 return getImproperInstance().format(f); 088 } 089 090 /** 091 * Returns the default complex format for the current locale. 092 * @return the default complex format. 093 */ 094 public static FractionFormat getImproperInstance() { 095 return getImproperInstance(Locale.getDefault()); 096 } 097 098 /** 099 * 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 }