FractionFormat.java

  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.  *      https://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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.fraction;

  22. import java.text.FieldPosition;
  23. import java.text.NumberFormat;
  24. import java.text.ParsePosition;
  25. import java.util.Locale;

  26. import org.hipparchus.exception.LocalizedCoreFormats;
  27. import org.hipparchus.exception.MathIllegalArgumentException;
  28. import org.hipparchus.exception.MathIllegalStateException;

  29. /**
  30.  * Formats a Fraction number in proper format or improper format.
  31.  * <p>
  32.  * The number format for each of the whole number, numerator and,
  33.  * denominator can be configured.
  34.  */
  35. public class FractionFormat extends AbstractFormat {

  36.     /** Serializable version identifier */
  37.     private static final long serialVersionUID = 20160323L;

  38.     /**
  39.      * Create an improper formatting instance with the default number format
  40.      * for the numerator and denominator.
  41.      */
  42.     public FractionFormat() {
  43.         // This constructor is intentionally empty. Nothing special is needed here.
  44.     }

  45.     /**
  46.      * Create an improper formatting instance with a custom number format for
  47.      * both the numerator and denominator.
  48.      * @param format the custom format for both the numerator and denominator.
  49.      * @throws org.hipparchus.exception.NullArgumentException if the provided format is null.
  50.      */
  51.     public FractionFormat(final NumberFormat format) {
  52.         super(format);
  53.     }

  54.     /**
  55.      * Create an improper formatting instance with a custom number format for
  56.      * the numerator and a custom number format for the denominator.
  57.      * @param numeratorFormat the custom format for the numerator.
  58.      * @param denominatorFormat the custom format for the denominator.
  59.      * @throws org.hipparchus.exception.NullArgumentException if either provided format is null.
  60.      */
  61.     public FractionFormat(final NumberFormat numeratorFormat,
  62.                           final NumberFormat denominatorFormat) {
  63.         super(numeratorFormat, denominatorFormat);
  64.     }

  65.     /**
  66.      * Get the set of locales for which complex formats are available.  This
  67.      * is the same set as the {@link NumberFormat} set.
  68.      * @return available complex format locales.
  69.      */
  70.     public static Locale[] getAvailableLocales() {
  71.         return NumberFormat.getAvailableLocales();
  72.     }

  73.     /**
  74.      * This static method calls formatFraction() on a default instance of
  75.      * FractionFormat.
  76.      *
  77.      * @param f Fraction object to format
  78.      * @return a formatted fraction in proper form.
  79.      */
  80.     public static String formatFraction(Fraction f) {
  81.         return getImproperInstance().format(f);
  82.     }

  83.     /**
  84.      * Returns the default complex format for the current locale.
  85.      * @return the default complex format.
  86.      */
  87.     public static FractionFormat getImproperInstance() {
  88.         return getImproperInstance(Locale.getDefault());
  89.     }

  90.     /**
  91.      * Returns the default complex format for the given locale.
  92.      * @param locale the specific locale used by the format.
  93.      * @return the complex format specific to the given locale.
  94.      */
  95.     public static FractionFormat getImproperInstance(final Locale locale) {
  96.         return new FractionFormat(getDefaultNumberFormat(locale));
  97.     }

  98.     /**
  99.      * Returns the default complex format for the current locale.
  100.      * @return the default complex format.
  101.      */
  102.     public static FractionFormat getProperInstance() {
  103.         return getProperInstance(Locale.getDefault());
  104.     }

  105.     /**
  106.      * Returns the default complex format for the given locale.
  107.      * @param locale the specific locale used by the format.
  108.      * @return the complex format specific to the given locale.
  109.      */
  110.     public static FractionFormat getProperInstance(final Locale locale) {
  111.         return new ProperFractionFormat(getDefaultNumberFormat(locale));
  112.     }

  113.     /**
  114.      * Create a default number format.  The default number format is based on
  115.      * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only
  116.      * customizing is the maximum number of fraction digits, which is set to 0.
  117.      * @return the default number format.
  118.      */
  119.     protected static NumberFormat getDefaultNumberFormat() {
  120.         return getDefaultNumberFormat(Locale.getDefault());
  121.     }

  122.     /**
  123.      * Formats a {@link Fraction} object to produce a string.  The fraction is
  124.      * output in improper format.
  125.      *
  126.      * @param fraction the object to format.
  127.      * @param toAppendTo where the text is to be appended
  128.      * @param pos On input: an alignment field, if desired. On output: the
  129.      * offsets of the alignment field
  130.      * @return the value passed in as toAppendTo.
  131.      */
  132.     public StringBuffer format(final Fraction fraction, // NOPMD - PMD false positive, we cannot have @Override here
  133.                                final StringBuffer toAppendTo, final FieldPosition pos) {

  134.         pos.setBeginIndex(0);
  135.         pos.setEndIndex(0);

  136.         getNumeratorFormat().format(fraction.getNumerator(), toAppendTo, pos);
  137.         toAppendTo.append(" / ");
  138.         getDenominatorFormat().format(fraction.getDenominator(), toAppendTo,
  139.             pos);

  140.         return toAppendTo;
  141.     }

  142.     /**
  143.      * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a
  144.      * {@link Fraction} object or a {@link Number} object.  Any other type of
  145.      * object will result in an {@link IllegalArgumentException} being thrown.
  146.      *
  147.      * @param obj the object to format.
  148.      * @param toAppendTo where the text is to be appended
  149.      * @param pos On input: an alignment field, if desired. On output: the
  150.      * offsets of the alignment field
  151.      * @return the value passed in as toAppendTo.
  152.      * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
  153.      * @throws MathIllegalStateException if the number cannot be converted to a fraction
  154.      * @throws MathIllegalArgumentException if <code>obj</code> is not a valid type.
  155.      */
  156.     @Override
  157.     public StringBuffer format(final Object obj,
  158.                                final StringBuffer toAppendTo, final FieldPosition pos)
  159.         throws MathIllegalArgumentException, MathIllegalStateException {

  160.         if (obj instanceof Fraction) {
  161.             return format((Fraction) obj, toAppendTo, pos);
  162.         } else if (obj instanceof Number) {
  163.             return format(new Fraction(((Number) obj).doubleValue()), toAppendTo, pos);
  164.         } else {
  165.             throw new MathIllegalArgumentException(LocalizedCoreFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION);
  166.         }

  167.     }

  168.     /**
  169.      * Parses a string to produce a {@link Fraction} object.
  170.      * @param source the string to parse
  171.      * @return the parsed {@link Fraction} object.
  172.      * @exception MathIllegalStateException if the beginning of the specified string
  173.      * cannot be parsed.
  174.      */
  175.     @Override
  176.     public Fraction parse(final String source) throws MathIllegalStateException {
  177.         final ParsePosition parsePosition = new ParsePosition(0);
  178.         final Fraction result = parse(source, parsePosition);
  179.         if (parsePosition.getIndex() == 0) {
  180.             throw new MathIllegalStateException(LocalizedCoreFormats.CANNOT_PARSE_AS_TYPE,
  181.                                                 source, parsePosition.getErrorIndex(), Fraction.class);
  182.         }
  183.         return result;
  184.     }

  185.     /**
  186.      * Parses a string to produce a {@link Fraction} object.  This method
  187.      * expects the string to be formatted as an improper fraction.
  188.      * @param source the string to parse
  189.      * @param pos input/output parsing parameter.
  190.      * @return the parsed {@link Fraction} object.
  191.      */
  192.     @Override
  193.     public Fraction parse(final String source, final ParsePosition pos) {
  194.         final int initialIndex = pos.getIndex();

  195.         // parse whitespace
  196.         parseAndIgnoreWhitespace(source, pos);

  197.         // parse numerator
  198.         final Number num = getNumeratorFormat().parse(source, pos);
  199.         if (num == null) {
  200.             // invalid integer number
  201.             // set index back to initial, error index should already be set
  202.             // character examined.
  203.             pos.setIndex(initialIndex);
  204.             return null;
  205.         }

  206.         // parse '/'
  207.         final int startIndex = pos.getIndex();
  208.         final char c = parseNextCharacter(source, pos);
  209.         switch (c) {
  210.         case 0 :
  211.             // no '/'
  212.             // return num as a fraction
  213.             return new Fraction(num.intValue(), 1);
  214.         case '/' :
  215.             // found '/', continue parsing denominator
  216.             break;
  217.         default :
  218.             // invalid '/'
  219.             // set index back to initial, error index should be the last
  220.             // character examined.
  221.             pos.setIndex(initialIndex);
  222.             pos.setErrorIndex(startIndex);
  223.             return null;
  224.         }

  225.         // parse whitespace
  226.         parseAndIgnoreWhitespace(source, pos);

  227.         // parse denominator
  228.         final Number den = getDenominatorFormat().parse(source, pos);
  229.         if (den == null) {
  230.             // invalid integer number
  231.             // set index back to initial, error index should already be set
  232.             // character examined.
  233.             pos.setIndex(initialIndex);
  234.             return null;
  235.         }

  236.         return new Fraction(num.intValue(), den.intValue());
  237.     }

  238. }