View Javadoc
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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.fraction;
24  
25  import java.text.FieldPosition;
26  import java.text.NumberFormat;
27  import java.text.ParsePosition;
28  import java.util.Locale;
29  
30  import org.hipparchus.exception.LocalizedCoreFormats;
31  import org.hipparchus.exception.MathIllegalArgumentException;
32  import org.hipparchus.exception.MathIllegalStateException;
33  
34  /**
35   * Formats a Fraction number in proper format or improper format.
36   * <p>
37   * The number format for each of the whole number, numerator and,
38   * denominator can be configured.
39   */
40  public class FractionFormat extends AbstractFormat {
41  
42      /** Serializable version identifier */
43      private static final long serialVersionUID = 20160323L;
44  
45      /**
46       * Create an improper formatting instance with the default number format
47       * for the numerator and denominator.
48       */
49      public FractionFormat() {
50          // This constructor is intentionally empty. Nothing special is needed here.
51      }
52  
53      /**
54       * Create an improper formatting instance with a custom number format for
55       * both the numerator and denominator.
56       * @param format the custom format for both the numerator and denominator.
57       * @throws org.hipparchus.exception.NullArgumentException if the provided format is null.
58       */
59      public FractionFormat(final NumberFormat format) {
60          super(format);
61      }
62  
63      /**
64       * Create an improper formatting instance with a custom number format for
65       * the numerator and a custom number format for the denominator.
66       * @param numeratorFormat the custom format for the numerator.
67       * @param denominatorFormat the custom format for the denominator.
68       * @throws org.hipparchus.exception.NullArgumentException if either provided format is null.
69       */
70      public FractionFormat(final NumberFormat numeratorFormat,
71                            final NumberFormat denominatorFormat) {
72          super(numeratorFormat, denominatorFormat);
73      }
74  
75      /**
76       * Get the set of locales for which complex formats are available.  This
77       * is the same set as the {@link NumberFormat} set.
78       * @return available complex format locales.
79       */
80      public static Locale[] getAvailableLocales() {
81          return NumberFormat.getAvailableLocales();
82      }
83  
84      /**
85       * This static method calls formatFraction() on a default instance of
86       * FractionFormat.
87       *
88       * @param f Fraction object to format
89       * @return a formatted fraction in proper form.
90       */
91      public static String formatFraction(Fraction f) {
92          return getImproperInstance().format(f);
93      }
94  
95      /**
96       * Returns the default complex format for the current locale.
97       * @return the default complex format.
98       */
99      public static FractionFormat getImproperInstance() {
100         return getImproperInstance(Locale.getDefault());
101     }
102 
103     /**
104      * Returns the default complex format for the given locale.
105      * @param locale the specific locale used by the format.
106      * @return the complex format specific to the given locale.
107      */
108     public static FractionFormat getImproperInstance(final Locale locale) {
109         return new FractionFormat(getDefaultNumberFormat(locale));
110     }
111 
112     /**
113      * Returns the default complex format for the current locale.
114      * @return the default complex format.
115      */
116     public static FractionFormat getProperInstance() {
117         return getProperInstance(Locale.getDefault());
118     }
119 
120     /**
121      * Returns the default complex format for the given locale.
122      * @param locale the specific locale used by the format.
123      * @return the complex format specific to the given locale.
124      */
125     public static FractionFormat getProperInstance(final Locale locale) {
126         return new ProperFractionFormat(getDefaultNumberFormat(locale));
127     }
128 
129     /**
130      * Create a default number format.  The default number format is based on
131      * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only
132      * customizing is the maximum number of fraction digits, which is set to 0.
133      * @return the default number format.
134      */
135     protected static NumberFormat getDefaultNumberFormat() {
136         return getDefaultNumberFormat(Locale.getDefault());
137     }
138 
139     /**
140      * Formats a {@link Fraction} object to produce a string.  The fraction is
141      * output in improper format.
142      *
143      * @param fraction the object to format.
144      * @param toAppendTo where the text is to be appended
145      * @param pos On input: an alignment field, if desired. On output: the
146      * offsets of the alignment field
147      * @return the value passed in as toAppendTo.
148      */
149     public StringBuffer format(final Fraction fraction, // NOPMD - PMD false positive, we cannot have @Override here
150                                final StringBuffer toAppendTo, final FieldPosition pos) {
151 
152         pos.setBeginIndex(0);
153         pos.setEndIndex(0);
154 
155         getNumeratorFormat().format(fraction.getNumerator(), toAppendTo, pos);
156         toAppendTo.append(" / ");
157         getDenominatorFormat().format(fraction.getDenominator(), toAppendTo,
158             pos);
159 
160         return toAppendTo;
161     }
162 
163     /**
164      * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a
165      * {@link Fraction} object or a {@link Number} object.  Any other type of
166      * object will result in an {@link IllegalArgumentException} being thrown.
167      *
168      * @param obj the object to format.
169      * @param toAppendTo where the text is to be appended
170      * @param pos On input: an alignment field, if desired. On output: the
171      * offsets of the alignment field
172      * @return the value passed in as toAppendTo.
173      * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
174      * @throws MathIllegalStateException if the number cannot be converted to a fraction
175      * @throws MathIllegalArgumentException if <code>obj</code> is not a valid type.
176      */
177     @Override
178     public StringBuffer format(final Object obj,
179                                final StringBuffer toAppendTo, final FieldPosition pos)
180         throws MathIllegalArgumentException, MathIllegalStateException {
181 
182         if (obj instanceof Fraction) {
183             return format((Fraction) obj, toAppendTo, pos);
184         } else if (obj instanceof Number) {
185             return format(new Fraction(((Number) obj).doubleValue()), toAppendTo, pos);
186         } else {
187             throw new MathIllegalArgumentException(LocalizedCoreFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION);
188         }
189 
190     }
191 
192     /**
193      * Parses a string to produce a {@link Fraction} object.
194      * @param source the string to parse
195      * @return the parsed {@link Fraction} object.
196      * @exception MathIllegalStateException if the beginning of the specified string
197      * cannot be parsed.
198      */
199     @Override
200     public Fraction parse(final String source) throws MathIllegalStateException {
201         final ParsePosition parsePosition = new ParsePosition(0);
202         final Fraction result = parse(source, parsePosition);
203         if (parsePosition.getIndex() == 0) {
204             throw new MathIllegalStateException(LocalizedCoreFormats.CANNOT_PARSE_AS_TYPE,
205                                                 source, parsePosition.getErrorIndex(), Fraction.class);
206         }
207         return result;
208     }
209 
210     /**
211      * Parses a string to produce a {@link Fraction} object.  This method
212      * expects the string to be formatted as an improper fraction.
213      * @param source the string to parse
214      * @param pos input/output parsing parameter.
215      * @return the parsed {@link Fraction} object.
216      */
217     @Override
218     public Fraction parse(final String source, final ParsePosition pos) {
219         final int initialIndex = pos.getIndex();
220 
221         // parse whitespace
222         parseAndIgnoreWhitespace(source, pos);
223 
224         // parse numerator
225         final Number num = getNumeratorFormat().parse(source, pos);
226         if (num == null) {
227             // invalid integer number
228             // set index back to initial, error index should already be set
229             // character examined.
230             pos.setIndex(initialIndex);
231             return null;
232         }
233 
234         // parse '/'
235         final int startIndex = pos.getIndex();
236         final char c = parseNextCharacter(source, pos);
237         switch (c) {
238         case 0 :
239             // no '/'
240             // return num as a fraction
241             return new Fraction(num.intValue(), 1);
242         case '/' :
243             // found '/', continue parsing denominator
244             break;
245         default :
246             // invalid '/'
247             // set index back to initial, error index should be the last
248             // character examined.
249             pos.setIndex(initialIndex);
250             pos.setErrorIndex(startIndex);
251             return null;
252         }
253 
254         // parse whitespace
255         parseAndIgnoreWhitespace(source, pos);
256 
257         // parse denominator
258         final Number den = getDenominatorFormat().parse(source, pos);
259         if (den == null) {
260             // invalid integer number
261             // set index back to initial, error index should already be set
262             // character examined.
263             pos.setIndex(initialIndex);
264             return null;
265         }
266 
267         return new Fraction(num.intValue(), den.intValue());
268     }
269 
270 }