1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.math.fraction;
17
18 import java.text.FieldPosition;
19 import java.text.NumberFormat;
20 import java.text.ParsePosition;
21
22 import org.apache.commons.math.util.MathUtils;
23
24 /**
25 * Formats a Fraction number in proper format. The number format for each of
26 * the whole number, numerator and, denominator can be configured.
27 *
28 * @since 1.1
29 * @version $Revision: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $
30 */
31 public class ProperFractionFormat extends FractionFormat {
32
33 /** Serializable version identifier */
34 private static final long serialVersionUID = -6337346779577272307L;
35
36 /** The format used for the whole number. */
37 private NumberFormat wholeFormat;
38
39 /**
40 * Create a proper formatting instance with the default number format for
41 * the whole, numerator, and denominator.
42 */
43 public ProperFractionFormat() {
44 this(getDefaultNumberFormat());
45 }
46
47 /**
48 * Create a proper formatting instance with a custom number format for the
49 * whole, numerator, and denominator.
50 * @param format the custom format for the whole, numerator, and
51 * denominator.
52 */
53 public ProperFractionFormat(NumberFormat format) {
54 this(format, (NumberFormat)format.clone(), (NumberFormat)format.clone());
55 }
56
57 /**
58 * Create a proper formatting instance with a custom number format for each
59 * of the whole, numerator, and denominator.
60 * @param wholeFormat the custom format for the whole.
61 * @param numeratorFormat the custom format for the numerator.
62 * @param denominatorFormat the custom format for the denominator.
63 */
64 public ProperFractionFormat(NumberFormat wholeFormat,
65 NumberFormat numeratorFormat,
66 NumberFormat denominatorFormat)
67 {
68 super(numeratorFormat, denominatorFormat);
69 setWholeFormat(wholeFormat);
70 }
71
72 /**
73 * Formats a {@link Fraction} object to produce a string. The fraction
74 * is output in proper format.
75 *
76 * @param fraction the object to format.
77 * @param toAppendTo where the text is to be appended
78 * @param pos On input: an alignment field, if desired. On output: the
79 * offsets of the alignment field
80 * @return the value passed in as toAppendTo.
81 */
82 public StringBuffer format(Fraction fraction, StringBuffer toAppendTo,
83 FieldPosition pos) {
84
85 pos.setBeginIndex(0);
86 pos.setEndIndex(0);
87
88 int num = fraction.getNumerator();
89 int den = fraction.getDenominator();
90 int whole = num / den;
91 num = num % den;
92
93 if (whole != 0) {
94 getWholeFormat().format(whole, toAppendTo, pos);
95 toAppendTo.append(' ');
96 num = Math.abs(num);
97 }
98 getNumeratorFormat().format(num, toAppendTo, pos);
99 toAppendTo.append(" / ");
100 getDenominatorFormat().format(den, toAppendTo,
101 pos);
102
103 return toAppendTo;
104 }
105
106 /**
107 * Access the whole format.
108 * @return the whole format.
109 */
110 public NumberFormat getWholeFormat() {
111 return wholeFormat;
112 }
113
114 /**
115 * Parses a string to produce a {@link Fraction} object. This method
116 * expects the string to be formatted as a proper fraction.
117 * @param source the string to parse
118 * @param pos input/ouput parsing parameter.
119 * @return the parsed {@link Fraction} object.
120 */
121 public Fraction parse(String source, ParsePosition pos) {
122
123 Fraction ret = super.parse(source, pos);
124 if (ret != null) {
125 return ret;
126 }
127
128 int initialIndex = pos.getIndex();
129
130
131 parseAndIgnoreWhitespace(source, pos);
132
133
134 Number whole = getWholeFormat().parse(source, pos);
135 if (whole == null) {
136
137
138
139 pos.setIndex(initialIndex);
140 return null;
141 }
142
143
144 parseAndIgnoreWhitespace(source, pos);
145
146
147 Number num = getNumeratorFormat().parse(source, pos);
148 if (num == null) {
149
150
151
152 pos.setIndex(initialIndex);
153 return null;
154 }
155
156
157 int startIndex = pos.getIndex();
158 char c = parseNextCharacter(source, pos);
159 switch (c) {
160 case 0 :
161
162
163 return new Fraction(num.intValue(), 1);
164 case '/' :
165
166 break;
167 default :
168
169
170
171 pos.setIndex(initialIndex);
172 pos.setErrorIndex(startIndex);
173 return null;
174 }
175
176
177 parseAndIgnoreWhitespace(source, pos);
178
179
180 Number den = getDenominatorFormat().parse(source, pos);
181 if (den == null) {
182
183
184
185 pos.setIndex(initialIndex);
186 return null;
187 }
188
189 int w = whole.intValue();
190 int n = num.intValue();
191 int d = den.intValue();
192 return new Fraction(((Math.abs(w) * d) + n) * MathUtils.sign(w), d);
193 }
194
195 /**
196 * Modify the whole format.
197 * @param format The new whole format value.
198 * @throws IllegalArgumentException if <code>format</code> is
199 * <code>null</code>.
200 */
201 public void setWholeFormat(NumberFormat format) {
202 if (format == null) {
203 throw new IllegalArgumentException(
204 "whole format can not be null.");
205 }
206 this.wholeFormat = format;
207 }
208 }