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