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 018package org.apache.commons.math3.fraction; 019 020import java.io.Serializable; 021import java.math.BigInteger; 022import java.text.FieldPosition; 023import java.text.NumberFormat; 024import java.text.ParsePosition; 025import java.util.Locale; 026 027import org.apache.commons.math3.exception.MathIllegalArgumentException; 028import org.apache.commons.math3.exception.MathParseException; 029import 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 */ 040public 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 MathIllegalArgumentException if <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 new MathIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION); 177 } 178 179 return ret; 180 } 181 182 /** 183 * Parses a string to produce a {@link BigFraction} object. 184 * @param source the string to parse 185 * @return the parsed {@link BigFraction} object. 186 * @exception MathParseException if the beginning of the specified string 187 * cannot be parsed. 188 */ 189 @Override 190 public BigFraction parse(final String source) throws MathParseException { 191 final ParsePosition parsePosition = new ParsePosition(0); 192 final BigFraction result = parse(source, parsePosition); 193 if (parsePosition.getIndex() == 0) { 194 throw new MathParseException(source, parsePosition.getErrorIndex(), BigFraction.class); 195 } 196 return result; 197 } 198 199 /** 200 * Parses a string to produce a {@link BigFraction} object. 201 * This method expects the string to be formatted as an improper BigFraction. 202 * @param source the string to parse 203 * @param pos input/output parsing parameter. 204 * @return the parsed {@link BigFraction} object. 205 */ 206 @Override 207 public BigFraction parse(final String source, final ParsePosition pos) { 208 final int initialIndex = pos.getIndex(); 209 210 // parse whitespace 211 parseAndIgnoreWhitespace(source, pos); 212 213 // parse numerator 214 final BigInteger num = parseNextBigInteger(source, pos); 215 if (num == null) { 216 // invalid integer number 217 // set index back to initial, error index should already be set 218 // character examined. 219 pos.setIndex(initialIndex); 220 return null; 221 } 222 223 // parse '/' 224 final int startIndex = pos.getIndex(); 225 final char c = parseNextCharacter(source, pos); 226 switch (c) { 227 case 0 : 228 // no '/' 229 // return num as a BigFraction 230 return new BigFraction(num); 231 case '/' : 232 // found '/', continue parsing denominator 233 break; 234 default : 235 // invalid '/' 236 // set index back to initial, error index should be the last 237 // character examined. 238 pos.setIndex(initialIndex); 239 pos.setErrorIndex(startIndex); 240 return null; 241 } 242 243 // parse whitespace 244 parseAndIgnoreWhitespace(source, pos); 245 246 // parse denominator 247 final BigInteger den = parseNextBigInteger(source, pos); 248 if (den == null) { 249 // invalid integer number 250 // set index back to initial, error index should already be set 251 // character examined. 252 pos.setIndex(initialIndex); 253 return null; 254 } 255 256 return new BigFraction(num, den); 257 } 258 259 /** 260 * Parses a string to produce a <code>BigInteger</code>. 261 * @param source the string to parse 262 * @param pos input/output parsing parameter. 263 * @return a parsed <code>BigInteger</code> or null if string does not 264 * contain a BigInteger at the specified position 265 */ 266 protected BigInteger parseNextBigInteger(final String source, 267 final ParsePosition pos) { 268 269 final int start = pos.getIndex(); 270 int end = (source.charAt(start) == '-') ? (start + 1) : start; 271 while((end < source.length()) && 272 Character.isDigit(source.charAt(end))) { 273 ++end; 274 } 275 276 try { 277 BigInteger n = new BigInteger(source.substring(start, end)); 278 pos.setIndex(end); 279 return n; 280 } catch (NumberFormatException nfe) { 281 pos.setErrorIndex(start); 282 return null; 283 } 284 285 } 286 287}