001: /*
002: * Copyright 2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.math.fraction;
017:
018: import java.text.FieldPosition;
019: import java.text.NumberFormat;
020: import java.text.ParsePosition;
021:
022: import org.apache.commons.math.util.MathUtils;
023:
024: /**
025: * Formats a Fraction number in proper format. The number format for each of
026: * the whole number, numerator and, denominator can be configured.
027: *
028: * @since 1.1
029: * @version $Revision: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $
030: */
031: public class ProperFractionFormat extends FractionFormat {
032:
033: /** Serializable version identifier */
034: private static final long serialVersionUID = -6337346779577272307L;
035:
036: /** The format used for the whole number. */
037: private NumberFormat wholeFormat;
038:
039: /**
040: * Create a proper formatting instance with the default number format for
041: * the whole, numerator, and denominator.
042: */
043: public ProperFractionFormat() {
044: this (getDefaultNumberFormat());
045: }
046:
047: /**
048: * Create a proper formatting instance with a custom number format for the
049: * whole, numerator, and denominator.
050: * @param format the custom format for the whole, numerator, and
051: * denominator.
052: */
053: public ProperFractionFormat(NumberFormat format) {
054: this (format, (NumberFormat) format.clone(),
055: (NumberFormat) format.clone());
056: }
057:
058: /**
059: * Create a proper formatting instance with a custom number format for each
060: * of the whole, numerator, and denominator.
061: * @param wholeFormat the custom format for the whole.
062: * @param numeratorFormat the custom format for the numerator.
063: * @param denominatorFormat the custom format for the denominator.
064: */
065: public ProperFractionFormat(NumberFormat wholeFormat,
066: NumberFormat numeratorFormat, NumberFormat denominatorFormat) {
067: super (numeratorFormat, denominatorFormat);
068: setWholeFormat(wholeFormat);
069: }
070:
071: /**
072: * Formats a {@link Fraction} object to produce a string. The fraction
073: * is output in proper format.
074: *
075: * @param fraction the object to format.
076: * @param toAppendTo where the text is to be appended
077: * @param pos On input: an alignment field, if desired. On output: the
078: * offsets of the alignment field
079: * @return the value passed in as toAppendTo.
080: */
081: public StringBuffer format(Fraction fraction,
082: StringBuffer toAppendTo, FieldPosition pos) {
083:
084: pos.setBeginIndex(0);
085: pos.setEndIndex(0);
086:
087: int num = fraction.getNumerator();
088: int den = fraction.getDenominator();
089: int whole = num / den;
090: num = num % den;
091:
092: if (whole != 0) {
093: getWholeFormat().format(whole, toAppendTo, pos);
094: toAppendTo.append(' ');
095: num = Math.abs(num);
096: }
097: getNumeratorFormat().format(num, toAppendTo, pos);
098: toAppendTo.append(" / ");
099: getDenominatorFormat().format(den, toAppendTo, pos);
100:
101: return toAppendTo;
102: }
103:
104: /**
105: * Access the whole format.
106: * @return the whole format.
107: */
108: public NumberFormat getWholeFormat() {
109: return wholeFormat;
110: }
111:
112: /**
113: * Parses a string to produce a {@link Fraction} object. This method
114: * expects the string to be formatted as a proper fraction.
115: * @param source the string to parse
116: * @param pos input/ouput parsing parameter.
117: * @return the parsed {@link Fraction} object.
118: */
119: public Fraction parse(String source, ParsePosition pos) {
120: // try to parse improper fraction
121: Fraction ret = super .parse(source, pos);
122: if (ret != null) {
123: return ret;
124: }
125:
126: int initialIndex = pos.getIndex();
127:
128: // parse whitespace
129: parseAndIgnoreWhitespace(source, pos);
130:
131: // parse whole
132: Number whole = getWholeFormat().parse(source, pos);
133: if (whole == null) {
134: // invalid integer number
135: // set index back to initial, error index should already be set
136: // character examined.
137: pos.setIndex(initialIndex);
138: return null;
139: }
140:
141: // parse whitespace
142: parseAndIgnoreWhitespace(source, pos);
143:
144: // parse numerator
145: Number num = getNumeratorFormat().parse(source, pos);
146: if (num == null) {
147: // invalid integer number
148: // set index back to initial, error index should already be set
149: // character examined.
150: pos.setIndex(initialIndex);
151: return null;
152: }
153:
154: // parse '/'
155: int startIndex = pos.getIndex();
156: char c = parseNextCharacter(source, pos);
157: switch (c) {
158: case 0:
159: // no '/'
160: // return num as a fraction
161: return new Fraction(num.intValue(), 1);
162: case '/':
163: // found '/', continue parsing denominator
164: break;
165: default:
166: // invalid '/'
167: // set index back to initial, error index should be the last
168: // character examined.
169: pos.setIndex(initialIndex);
170: pos.setErrorIndex(startIndex);
171: return null;
172: }
173:
174: // parse whitespace
175: parseAndIgnoreWhitespace(source, pos);
176:
177: // parse denominator
178: Number den = getDenominatorFormat().parse(source, pos);
179: if (den == null) {
180: // invalid integer number
181: // set index back to initial, error index should already be set
182: // character examined.
183: pos.setIndex(initialIndex);
184: return null;
185: }
186:
187: int w = whole.intValue();
188: int n = num.intValue();
189: int d = den.intValue();
190: return new Fraction(
191: ((Math.abs(w) * d) + n) * MathUtils.sign(w), d);
192: }
193:
194: /**
195: * Modify the whole format.
196: * @param format The new whole format value.
197: * @throws IllegalArgumentException if <code>format</code> is
198: * <code>null</code>.
199: */
200: public void setWholeFormat(NumberFormat format) {
201: if (format == null) {
202: throw new IllegalArgumentException(
203: "whole format can not be null.");
204: }
205: this.wholeFormat = format;
206: }
207: }
|