001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.fraction;
019    
020    import java.io.Serializable;
021    import java.text.FieldPosition;
022    import java.text.NumberFormat;
023    import java.text.ParsePosition;
024    import java.util.Locale;
025    
026    import org.apache.commons.math.MathRuntimeException;
027    
028    /**
029     * Common part shared by both {@link FractionFormat} and {@link BigFractionFormat}.
030     * @version $Revision: 780674 $ $Date: 2009-06-01 11:10:55 -0400 (Mon, 01 Jun 2009) $
031     * @since 2.0
032     */
033    public abstract class AbstractFormat extends NumberFormat implements Serializable {
034    
035        /** Serializable version identifier. */
036        private static final long serialVersionUID = -6981118387974191891L;
037    
038        /** The format used for the denominator. */
039        protected NumberFormat denominatorFormat;
040    
041        /** The format used for the numerator. */
042        protected NumberFormat numeratorFormat;
043    
044        /**
045         * Create an improper formatting instance with the default number format
046         * for the numerator and denominator.  
047         */
048        protected AbstractFormat() {
049            this(getDefaultNumberFormat());
050        }
051    
052        /**
053         * Create an improper formatting instance with a custom number format for
054         * both the numerator and denominator.
055         * @param format the custom format for both the numerator and denominator.
056         */
057        protected AbstractFormat(final NumberFormat format) {
058            this(format, (NumberFormat) format.clone());
059        }
060    
061        /**
062         * Create an improper formatting instance with a custom number format for
063         * the numerator and a custom number format for the denominator.
064         * @param numeratorFormat the custom format for the numerator.
065         * @param denominatorFormat the custom format for the denominator.
066         */
067        protected AbstractFormat(final NumberFormat numeratorFormat,
068                                 final NumberFormat denominatorFormat) {
069            this.numeratorFormat   = numeratorFormat;
070            this.denominatorFormat = denominatorFormat;
071        }
072    
073        /**
074         * Create a default number format.  The default number format is based on
075         * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only
076         * customizing is the maximum number of BigFraction digits, which is set to 0.  
077         * @return the default number format.
078         */
079        protected static NumberFormat getDefaultNumberFormat() {
080            return getDefaultNumberFormat(Locale.getDefault());
081        }
082    
083        /**
084         * Create a default number format.  The default number format is based on
085         * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only
086         * customizing is the maximum number of BigFraction digits, which is set to 0.  
087         * @param locale the specific locale used by the format.
088         * @return the default number format specific to the given locale.
089         */
090        protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
091            final NumberFormat nf = NumberFormat.getNumberInstance(locale);
092            nf.setMaximumFractionDigits(0);
093            nf.setParseIntegerOnly(true);
094            return nf;
095        }
096    
097        /**
098         * Access the denominator format.
099         * @return the denominator format.
100         */
101        public NumberFormat getDenominatorFormat() {
102            return denominatorFormat;
103        }
104    
105        /**
106         * Access the numerator format.
107         * @return the numerator format.
108         */
109        public NumberFormat getNumeratorFormat() {
110            return numeratorFormat;
111        }
112    
113        /**
114         * Modify the denominator format.
115         * @param format the new denominator format value.
116         * @throws IllegalArgumentException if <code>format</code> is
117         *         <code>null</code>.
118         */
119        public void setDenominatorFormat(final NumberFormat format) {
120            if (format == null) {
121                throw MathRuntimeException.createIllegalArgumentException(
122                    "denominator format can not be null");
123            }
124            this.denominatorFormat = format;
125        }
126    
127        /**
128         * Modify the numerator format.
129         * @param format the new numerator format value.
130         * @throws IllegalArgumentException if <code>format</code> is
131         *         <code>null</code>.
132         */
133        public void setNumeratorFormat(final NumberFormat format) {
134            if (format == null) {
135                throw MathRuntimeException.createIllegalArgumentException(
136                    "numerator format can not be null");
137            }
138            this.numeratorFormat = format;
139        }
140    
141        /**
142         * Parses <code>source</code> until a non-whitespace character is found.
143         * @param source the string to parse
144         * @param pos input/ouput parsing parameter.  On output, <code>pos</code>
145         *        holds the index of the next non-whitespace character.
146         */
147        protected static void parseAndIgnoreWhitespace(final String source,
148                                                       final ParsePosition pos) {
149            parseNextCharacter(source, pos);
150            pos.setIndex(pos.getIndex() - 1);
151        }
152    
153        /**
154         * Parses <code>source</code> until a non-whitespace character is found.
155         * @param source the string to parse
156         * @param pos input/ouput parsing parameter.
157         * @return the first non-whitespace character.
158         */
159        protected static char parseNextCharacter(final String source,
160                                                 final ParsePosition pos) {
161             int index = pos.getIndex();
162             final int n = source.length();
163             char ret = 0;
164    
165             if (index < n) {
166                 char c;
167                 do {
168                     c = source.charAt(index++);
169                 } while (Character.isWhitespace(c) && index < n);
170                 pos.setIndex(index);
171             
172                 if (index < n) {
173                     ret = c;
174                 }
175             }
176             
177             return ret;
178        }
179    
180        /**
181         * Formats a double value as a fraction and appends the result to a StringBuffer. 
182         *
183         * @param value the double value to format
184         * @param buffer StringBuffer to append to
185         * @param position On input: an alignment field, if desired. On output: the
186         *            offsets of the alignment field
187         * @return a reference to the appended buffer
188         * @see #format(Object, StringBuffer, FieldPosition)
189         */
190        @Override
191        public StringBuffer format(final double value,
192                                   final StringBuffer buffer, final FieldPosition position) {
193            return format(Double.valueOf(value), buffer, position);
194        }
195    
196        
197        /**
198         * Formats a long value as a fraction and appends the result to a StringBuffer. 
199         *
200         * @param value the long value to format
201         * @param buffer StringBuffer to append to
202         * @param position On input: an alignment field, if desired. On output: the
203         *            offsets of the alignment field
204         * @return a reference to the appended buffer
205         * @see #format(Object, StringBuffer, FieldPosition)
206         */
207        @Override
208        public StringBuffer format(final long value,
209                                   final StringBuffer buffer, final FieldPosition position) {
210            return format(Long.valueOf(value), buffer, position);
211        }
212    
213    }