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 18package org.apache.commons.math.fraction; 19 20import java.io.Serializable; 21import java.math.BigInteger; 22import java.text.FieldPosition; 23import java.text.NumberFormat; 24import java.text.ParseException; 25import java.text.ParsePosition; 26import java.util.Locale; 27 28import org.apache.commons.math.MathRuntimeException; 29import org.apache.commons.math.exception.util.LocalizedFormats; 30 31/** 32 * Formats a BigFraction number in proper format or improper format. 33 * <p> 34 * The number format for each of the whole number, numerator and, 35 * denominator can be configured. 36 * </p> 37 * 38 * @since 2.0 39 * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $ 40 */ 41public class BigFractionFormat extends AbstractFormat implements Serializable { 42 43 /** Serializable version identifier */ 44 private static final long serialVersionUID = -2932167925527338976L; 45 46 /** 47 * Create an improper formatting instance with the default number format 48 * for the numerator and denominator. 49 */ 50 public BigFractionFormat() { 51 } 52 53 /** 54 * Create an improper formatting instance with a custom number format for 55 * both the numerator and denominator. 56 * @param format the custom format for both the numerator and denominator. 57 */ 58 public BigFractionFormat(final NumberFormat format) { 59 super(format); 60 } 61 62 /** 63 * Create an improper formatting instance with a custom number format for 64 * the numerator and a custom number format for the denominator. 65 * @param numeratorFormat the custom format for the numerator. 66 * @param denominatorFormat the custom format for the denominator. 67 */ 68 public BigFractionFormat(final NumberFormat numeratorFormat, 69 final NumberFormat denominatorFormat) { 70 super(numeratorFormat, denominatorFormat); 71 } 72 73 /** 74 * Get the set of locales for which complex formats are available. This 75 * is the same set as the {@link NumberFormat} set. 76 * @return available complex format locales. 77 */ 78 public static Locale[] getAvailableLocales() { 79 return NumberFormat.getAvailableLocales(); 80 } 81 82 /** 83 * This static method calls formatBigFraction() on a default instance of 84 * BigFractionFormat. 85 * 86 * @param f BigFraction object to format 87 * @return A formatted BigFraction in proper form. 88 */ 89 public static String formatBigFraction(final BigFraction f) { 90 return getImproperInstance().format(f); 91 } 92 93 /** 94 * Returns the default complex format for the current locale. 95 * @return the default complex format. 96 */ 97 public static BigFractionFormat getImproperInstance() { 98 return getImproperInstance(Locale.getDefault()); 99 } 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 IllegalArgumentException is <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 MathRuntimeException.createIllegalArgumentException( 178 LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION); 179 } 180 181 return ret; 182 } 183 184 /** 185 * Parses a string to produce a {@link BigFraction} object. 186 * @param source the string to parse 187 * @return the parsed {@link BigFraction} object. 188 * @exception ParseException if the beginning of the specified string 189 * cannot be parsed. 190 */ 191 @Override 192 public BigFraction parse(final String source) throws ParseException { 193 final ParsePosition parsePosition = new ParsePosition(0); 194 final BigFraction result = parse(source, parsePosition); 195 if (parsePosition.getIndex() == 0) { 196 throw MathRuntimeException.createParseException( 197 parsePosition.getErrorIndex(), 198 LocalizedFormats.UNPARSEABLE_FRACTION_NUMBER, source); 199 } 200 return result; 201 } 202 203 /** 204 * Parses a string to produce a {@link BigFraction} object. 205 * This method expects the string to be formatted as an improper BigFraction. 206 * @param source the string to parse 207 * @param pos input/ouput parsing parameter. 208 * @return the parsed {@link BigFraction} object. 209 */ 210 @Override 211 public BigFraction parse(final String source, final ParsePosition pos) { 212 final int initialIndex = pos.getIndex(); 213 214 // parse whitespace 215 parseAndIgnoreWhitespace(source, pos); 216 217 // parse numerator 218 final BigInteger num = parseNextBigInteger(source, pos); 219 if (num == null) { 220 // invalid integer number 221 // set index back to initial, error index should already be set 222 // character examined. 223 pos.setIndex(initialIndex); 224 return null; 225 } 226 227 // parse '/' 228 final int startIndex = pos.getIndex(); 229 final char c = parseNextCharacter(source, pos); 230 switch (c) { 231 case 0 : 232 // no '/' 233 // return num as a BigFraction 234 return new BigFraction(num); 235 case '/' : 236 // found '/', continue parsing denominator 237 break; 238 default : 239 // invalid '/' 240 // set index back to initial, error index should be the last 241 // character examined. 242 pos.setIndex(initialIndex); 243 pos.setErrorIndex(startIndex); 244 return null; 245 } 246 247 // parse whitespace 248 parseAndIgnoreWhitespace(source, pos); 249 250 // parse denominator 251 final BigInteger den = parseNextBigInteger(source, pos); 252 if (den == null) { 253 // invalid integer number 254 // set index back to initial, error index should already be set 255 // character examined. 256 pos.setIndex(initialIndex); 257 return null; 258 } 259 260 return new BigFraction(num, den); 261 } 262 263 /** 264 * Parses a string to produce a <code>BigInteger</code>. 265 * @param source the string to parse 266 * @param pos input/ouput parsing parameter. 267 * @return a parsed <code>BigInteger</code> or null if string does not 268 * contain a BigInteger at the specified position 269 */ 270 protected BigInteger parseNextBigInteger(final String source, 271 final ParsePosition pos) { 272 273 final int start = pos.getIndex(); 274 int end = (source.charAt(start) == '-') ? (start + 1) : start; 275 while((end < source.length()) && 276 Character.isDigit(source.charAt(end))) { 277 ++end; 278 } 279 280 try { 281 BigInteger n = new BigInteger(source.substring(start, end)); 282 pos.setIndex(end); 283 return n; 284 } catch (NumberFormatException nfe) { 285 pos.setErrorIndex(start); 286 return null; 287 } 288 289 } 290 291} 292