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.math3.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.ParsePosition; 025 import java.util.Locale; 026 027 import org.apache.commons.math3.exception.MathIllegalArgumentException; 028 import org.apache.commons.math3.exception.MathParseException; 029 import org.apache.commons.math3.exception.util.LocalizedFormats; 030 031 /** 032 * Formats a BigFraction number in proper format or improper format. 033 * <p> 034 * The number format for each of the whole number, numerator and, 035 * denominator can be configured. 036 * </p> 037 * 038 * @since 2.0 039 * @version $Id: BigFractionFormat.java 1416643 2012-12-03 19:37:14Z tn $ 040 */ 041 public class BigFractionFormat extends AbstractFormat implements Serializable { 042 043 /** Serializable version identifier */ 044 private static final long serialVersionUID = -2932167925527338976L; 045 046 /** 047 * Create an improper formatting instance with the default number format 048 * for the numerator and denominator. 049 */ 050 public BigFractionFormat() { 051 } 052 053 /** 054 * Create an improper formatting instance with a custom number format for 055 * both the numerator and denominator. 056 * @param format the custom format for both the numerator and denominator. 057 */ 058 public BigFractionFormat(final NumberFormat format) { 059 super(format); 060 } 061 062 /** 063 * Create an improper formatting instance with a custom number format for 064 * the numerator and a custom number format for the denominator. 065 * @param numeratorFormat the custom format for the numerator. 066 * @param denominatorFormat the custom format for the denominator. 067 */ 068 public BigFractionFormat(final NumberFormat numeratorFormat, 069 final NumberFormat denominatorFormat) { 070 super(numeratorFormat, denominatorFormat); 071 } 072 073 /** 074 * Get the set of locales for which complex formats are available. This 075 * is the same set as the {@link NumberFormat} set. 076 * @return available complex format locales. 077 */ 078 public static Locale[] getAvailableLocales() { 079 return NumberFormat.getAvailableLocales(); 080 } 081 082 /** 083 * This static method calls formatBigFraction() on a default instance of 084 * BigFractionFormat. 085 * 086 * @param f BigFraction object to format 087 * @return A formatted BigFraction in proper form. 088 */ 089 public static String formatBigFraction(final BigFraction f) { 090 return getImproperInstance().format(f); 091 } 092 093 /** 094 * Returns the default complex format for the current locale. 095 * @return the default complex format. 096 */ 097 public static BigFractionFormat getImproperInstance() { 098 return getImproperInstance(Locale.getDefault()); 099 } 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 MathIllegalArgumentException if <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 new MathIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION); 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 MathParseException if the beginning of the specified string 188 * cannot be parsed. 189 */ 190 @Override 191 public BigFraction parse(final String source) throws MathParseException { 192 final ParsePosition parsePosition = new ParsePosition(0); 193 final BigFraction result = parse(source, parsePosition); 194 if (parsePosition.getIndex() == 0) { 195 throw new MathParseException(source, parsePosition.getErrorIndex(), BigFraction.class); 196 } 197 return result; 198 } 199 200 /** 201 * Parses a string to produce a {@link BigFraction} object. 202 * This method expects the string to be formatted as an improper BigFraction. 203 * @param source the string to parse 204 * @param pos input/output parsing parameter. 205 * @return the parsed {@link BigFraction} object. 206 */ 207 @Override 208 public BigFraction parse(final String source, final ParsePosition pos) { 209 final int initialIndex = pos.getIndex(); 210 211 // parse whitespace 212 parseAndIgnoreWhitespace(source, pos); 213 214 // parse numerator 215 final BigInteger num = parseNextBigInteger(source, pos); 216 if (num == null) { 217 // invalid integer number 218 // set index back to initial, error index should already be set 219 // character examined. 220 pos.setIndex(initialIndex); 221 return null; 222 } 223 224 // parse '/' 225 final int startIndex = pos.getIndex(); 226 final char c = parseNextCharacter(source, pos); 227 switch (c) { 228 case 0 : 229 // no '/' 230 // return num as a BigFraction 231 return new BigFraction(num); 232 case '/' : 233 // found '/', continue parsing denominator 234 break; 235 default : 236 // invalid '/' 237 // set index back to initial, error index should be the last 238 // character examined. 239 pos.setIndex(initialIndex); 240 pos.setErrorIndex(startIndex); 241 return null; 242 } 243 244 // parse whitespace 245 parseAndIgnoreWhitespace(source, pos); 246 247 // parse denominator 248 final BigInteger den = parseNextBigInteger(source, pos); 249 if (den == null) { 250 // invalid integer number 251 // set index back to initial, error index should already be set 252 // character examined. 253 pos.setIndex(initialIndex); 254 return null; 255 } 256 257 return new BigFraction(num, den); 258 } 259 260 /** 261 * Parses a string to produce a <code>BigInteger</code>. 262 * @param source the string to parse 263 * @param pos input/output parsing parameter. 264 * @return a parsed <code>BigInteger</code> or null if string does not 265 * contain a BigInteger at the specified position 266 */ 267 protected BigInteger parseNextBigInteger(final String source, 268 final ParsePosition pos) { 269 270 final int start = pos.getIndex(); 271 int end = (source.charAt(start) == '-') ? (start + 1) : start; 272 while((end < source.length()) && 273 Character.isDigit(source.charAt(end))) { 274 ++end; 275 } 276 277 try { 278 BigInteger n = new BigInteger(source.substring(start, end)); 279 pos.setIndex(end); 280 return n; 281 } catch (NumberFormatException nfe) { 282 pos.setErrorIndex(start); 283 return null; 284 } 285 286 } 287 288 }