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