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.math3.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.math3.exception.NullArgumentException;
027    import org.apache.commons.math3.exception.util.LocalizedFormats;
028    
029    /**
030     * Common part shared by both {@link FractionFormat} and {@link BigFractionFormat}.
031     * @version $Id: AbstractFormat.java 1416643 2012-12-03 19:37:14Z tn $
032     * @since 2.0
033     */
034    public abstract class AbstractFormat extends NumberFormat implements Serializable {
035    
036        /** Serializable version identifier. */
037        private static final long serialVersionUID = -6981118387974191891L;
038    
039        /** The format used for the denominator. */
040        private NumberFormat denominatorFormat;
041    
042        /** The format used for the numerator. */
043        private NumberFormat numeratorFormat;
044    
045        /**
046         * Create an improper formatting instance with the default number format
047         * for the numerator and denominator.
048         */
049        protected AbstractFormat() {
050            this(getDefaultNumberFormat());
051        }
052    
053        /**
054         * Create an improper formatting instance with a custom number format for
055         * both the numerator and denominator.
056         * @param format the custom format for both the numerator and denominator.
057         */
058        protected AbstractFormat(final NumberFormat format) {
059            this(format, (NumberFormat) format.clone());
060        }
061    
062        /**
063         * Create an improper formatting instance with a custom number format for
064         * the numerator and a custom number format for the denominator.
065         * @param numeratorFormat the custom format for the numerator.
066         * @param denominatorFormat the custom format for the denominator.
067         */
068        protected AbstractFormat(final NumberFormat numeratorFormat,
069                                 final NumberFormat denominatorFormat) {
070            this.numeratorFormat   = numeratorFormat;
071            this.denominatorFormat = denominatorFormat;
072        }
073    
074        /**
075         * Create a default number format.  The default number format is based on
076         * {@link NumberFormat#getNumberInstance(java.util.Locale)}. The only
077         * customization is the maximum number of BigFraction digits, which is set to 0.
078         * @return the default number format.
079         */
080        protected static NumberFormat getDefaultNumberFormat() {
081            return getDefaultNumberFormat(Locale.getDefault());
082        }
083    
084        /**
085         * Create a default number format.  The default number format is based on
086         * {@link NumberFormat#getNumberInstance(java.util.Locale)}. The only
087         * customization is the maximum number of BigFraction digits, which is set to 0.
088         * @param locale the specific locale used by the format.
089         * @return the default number format specific to the given locale.
090         */
091        protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
092            final NumberFormat nf = NumberFormat.getNumberInstance(locale);
093            nf.setMaximumFractionDigits(0);
094            nf.setParseIntegerOnly(true);
095            return nf;
096        }
097    
098        /**
099         * Access the denominator format.
100         * @return the denominator format.
101         */
102        public NumberFormat getDenominatorFormat() {
103            return denominatorFormat;
104        }
105    
106        /**
107         * Access the numerator format.
108         * @return the numerator format.
109         */
110        public NumberFormat getNumeratorFormat() {
111            return numeratorFormat;
112        }
113    
114        /**
115         * Modify the denominator format.
116         * @param format the new denominator format value.
117         * @throws NullArgumentException if {@code format} is {@code null}.
118         */
119        public void setDenominatorFormat(final NumberFormat format) {
120            if (format == null) {
121                throw new NullArgumentException(LocalizedFormats.DENOMINATOR_FORMAT);
122            }
123            this.denominatorFormat = format;
124        }
125    
126        /**
127         * Modify the numerator format.
128         * @param format the new numerator format value.
129         * @throws NullArgumentException if {@code format} is {@code null}.
130         */
131        public void setNumeratorFormat(final NumberFormat format) {
132            if (format == null) {
133                throw new NullArgumentException(LocalizedFormats.NUMERATOR_FORMAT);
134            }
135            this.numeratorFormat = format;
136        }
137    
138        /**
139         * Parses <code>source</code> until a non-whitespace character is found.
140         * @param source the string to parse
141         * @param pos input/output parsing parameter.  On output, <code>pos</code>
142         *        holds the index of the next non-whitespace character.
143         */
144        protected static void parseAndIgnoreWhitespace(final String source,
145                                                       final ParsePosition pos) {
146            parseNextCharacter(source, pos);
147            pos.setIndex(pos.getIndex() - 1);
148        }
149    
150        /**
151         * Parses <code>source</code> until a non-whitespace character is found.
152         * @param source the string to parse
153         * @param pos input/output parsing parameter.
154         * @return the first non-whitespace character.
155         */
156        protected static char parseNextCharacter(final String source,
157                                                 final ParsePosition pos) {
158             int index = pos.getIndex();
159             final int n = source.length();
160             char ret = 0;
161    
162             if (index < n) {
163                 char c;
164                 do {
165                     c = source.charAt(index++);
166                 } while (Character.isWhitespace(c) && index < n);
167                 pos.setIndex(index);
168    
169                 if (index < n) {
170                     ret = c;
171                 }
172             }
173    
174             return ret;
175        }
176    
177        /**
178         * Formats a double value as a fraction and appends the result to a StringBuffer.
179         *
180         * @param value the double value to format
181         * @param buffer StringBuffer to append to
182         * @param position On input: an alignment field, if desired. On output: the
183         *            offsets of the alignment field
184         * @return a reference to the appended buffer
185         * @see #format(Object, StringBuffer, FieldPosition)
186         */
187        @Override
188        public StringBuffer format(final double value,
189                                   final StringBuffer buffer, final FieldPosition position) {
190            return format(Double.valueOf(value), buffer, position);
191        }
192    
193    
194        /**
195         * Formats a long value as a fraction and appends the result to a StringBuffer.
196         *
197         * @param value the long value to format
198         * @param buffer StringBuffer to append to
199         * @param position On input: an alignment field, if desired. On output: the
200         *            offsets of the alignment field
201         * @return a reference to the appended buffer
202         * @see #format(Object, StringBuffer, FieldPosition)
203         */
204        @Override
205        public StringBuffer format(final long value,
206                                   final StringBuffer buffer, final FieldPosition position) {
207            return format(Long.valueOf(value), buffer, position);
208        }
209    
210    }