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.io.Serializable; 21 import java.math.BigInteger; 22 import java.text.FieldPosition; 23 import java.text.NumberFormat; 24 import java.text.ParseException; 25 import java.text.ParsePosition; 26 import java.util.Locale; 27 28 import org.apache.commons.math.MathRuntimeException; 29 30 /** 31 * Formats a BigFraction number in proper format or improper format. 32 * <p> 33 * The number format for each of the whole number, numerator and, 34 * denominator can be configured. 35 * </p> 36 * 37 * @since 2.0 38 * @version $Revision: 762087 $ $Date: 2009-04-05 10:20:18 -0400 (Sun, 05 Apr 2009) $ 39 */ 40 public class BigFractionFormat extends AbstractFormat implements Serializable { 41 42 /** Serializable version identifier */ 43 private static final long serialVersionUID = -2932167925527338976L; 44 45 /** 46 * Create an improper formatting instance with the default number format 47 * for the numerator and denominator. 48 */ 49 public BigFractionFormat() { 50 } 51 52 /** 53 * Create an improper formatting instance with a custom number format for 54 * both the numerator and denominator. 55 * @param format the custom format for both the numerator and denominator. 56 */ 57 public BigFractionFormat(final NumberFormat format) { 58 super(format); 59 } 60 61 /** 62 * Create an improper formatting instance with a custom number format for 63 * the numerator and a custom number format for the denominator. 64 * @param numeratorFormat the custom format for the numerator. 65 * @param denominatorFormat the custom format for the denominator. 66 */ 67 public BigFractionFormat(final NumberFormat numeratorFormat, 68 final NumberFormat denominatorFormat) { 69 super(numeratorFormat, denominatorFormat); 70 } 71 72 /** 73 * Get the set of locales for which complex formats are available. This 74 * is the same set as the {@link NumberFormat} set. 75 * @return available complex format locales. 76 */ 77 public static Locale[] getAvailableLocales() { 78 return NumberFormat.getAvailableLocales(); 79 } 80 81 /** 82 * This static method calls formatBigFraction() on a default instance of 83 * BigFractionFormat. 84 * 85 * @param f BigFraction object to format 86 * @return A formatted BigFraction in proper form. 87 */ 88 public static String formatBigFraction(final BigFraction f) { 89 return getImproperInstance().format(f); 90 } 91 92 /** 93 * Returns the default complex format for the current locale. 94 * @return the default complex format. 95 */ 96 public static BigFractionFormat getImproperInstance() { 97 return getImproperInstance(Locale.getDefault()); 98 } 99 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 }