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    package org.apache.commons.math.util;
018    
019    import java.text.FieldPosition;
020    import java.text.Format;
021    import java.text.NumberFormat;
022    import java.text.ParsePosition;
023    import java.util.Locale;
024    
025    /**
026     * Base class for formatters of composite objects (complex numbers, vectors ...).
027     *
028     * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 d??c. 2010) $
029     */
030    public abstract class CompositeFormat extends Format {
031    
032        /** Serializable version identifier. */
033        private static final long serialVersionUID = 5358685519349262494L;
034    
035        /**
036         * Create a default number format.  The default number format is based on
037         * {@link NumberFormat#getInstance()} with the only customizing that the
038         * maximum number of fraction digits is set to 2.
039         * @return the default number format.
040         */
041        protected static NumberFormat getDefaultNumberFormat() {
042            return getDefaultNumberFormat(Locale.getDefault());
043        }
044    
045        /**
046         * Create a default number format.  The default number format is based on
047         * {@link NumberFormat#getInstance(java.util.Locale)} with the only
048         * customizing that the maximum number of fraction digits is set to 2.
049         * @param locale the specific locale used by the format.
050         * @return the default number format specific to the given locale.
051         */
052        protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
053            final NumberFormat nf = NumberFormat.getInstance(locale);
054            nf.setMaximumFractionDigits(2);
055            return nf;
056        }
057    
058        /**
059         * Parses <code>source</code> until a non-whitespace character is found.
060         *
061         * @param source the string to parse
062         * @param pos input/ouput parsing parameter.  On output, <code>pos</code>
063         *        holds the index of the next non-whitespace character.
064         */
065        protected void parseAndIgnoreWhitespace(final String source,
066                                                final ParsePosition pos) {
067            parseNextCharacter(source, pos);
068            pos.setIndex(pos.getIndex() - 1);
069        }
070    
071        /**
072         * Parses <code>source</code> until a non-whitespace character is found.
073         *
074         * @param source the string to parse
075         * @param pos input/ouput parsing parameter.
076         * @return the first non-whitespace character.
077         */
078        protected char parseNextCharacter(final String source,
079                                          final ParsePosition pos) {
080             int index = pos.getIndex();
081             final int n = source.length();
082             char ret = 0;
083    
084             if (index < n) {
085                 char c;
086                 do {
087                     c = source.charAt(index++);
088                 } while (Character.isWhitespace(c) && index < n);
089                 pos.setIndex(index);
090    
091                 if (index < n) {
092                     ret = c;
093                 }
094             }
095    
096             return ret;
097        }
098    
099        /**
100         * Parses <code>source</code> for special double values.  These values
101         * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
102         *
103         * @param source the string to parse
104         * @param value the special value to parse.
105         * @param pos input/ouput parsing parameter.
106         * @return the special number.
107         */
108        private Number parseNumber(final String source, final double value,
109                                   final ParsePosition pos) {
110            Number ret = null;
111    
112            StringBuilder sb = new StringBuilder();
113            sb.append('(');
114            sb.append(value);
115            sb.append(')');
116    
117            final int n = sb.length();
118            final int startIndex = pos.getIndex();
119            final int endIndex = startIndex + n;
120            if (endIndex < source.length()) {
121                if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
122                    ret = Double.valueOf(value);
123                    pos.setIndex(endIndex);
124                }
125            }
126    
127            return ret;
128        }
129    
130        /**
131         * Parses <code>source</code> for a number.  This method can parse normal,
132         * numeric values as well as special values.  These special values include
133         * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
134         *
135         * @param source the string to parse
136         * @param format the number format used to parse normal, numeric values.
137         * @param pos input/ouput parsing parameter.
138         * @return the parsed number.
139         */
140        protected Number parseNumber(final String source, final NumberFormat format,
141                                     final ParsePosition pos) {
142            final int startIndex = pos.getIndex();
143            Number number = format.parse(source, pos);
144            final int endIndex = pos.getIndex();
145    
146            // check for error parsing number
147            if (startIndex == endIndex) {
148                // try parsing special numbers
149                final double[] special = {
150                    Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
151                };
152                for (int i = 0; i < special.length; ++i) {
153                    number = parseNumber(source, special[i], pos);
154                    if (number != null) {
155                        break;
156                    }
157                }
158            }
159    
160            return number;
161        }
162    
163        /**
164         * Parse <code>source</code> for an expected fixed string.
165         * @param source the string to parse
166         * @param expected expected string
167         * @param pos input/ouput parsing parameter.
168         * @return true if the expected string was there
169         */
170        protected boolean parseFixedstring(final String source, final String expected,
171                                           final ParsePosition pos) {
172    
173            final int startIndex = pos.getIndex();
174            final int endIndex = startIndex + expected.length();
175            if ((startIndex >= source.length()) ||
176                (endIndex > source.length()) ||
177                (source.substring(startIndex, endIndex).compareTo(expected) != 0)) {
178                // set index back to start, error index should be the start index
179                pos.setIndex(startIndex);
180                pos.setErrorIndex(startIndex);
181                return false;
182            }
183    
184            // the string was here
185            pos.setIndex(endIndex);
186            return true;
187    
188        }
189    
190        /**
191         * Formats a double value to produce a string.  In general, the value is
192         * formatted using the formatting rules of <code>format</code>.  There are
193         * three exceptions to this:
194         * <ol>
195         * <li>NaN is formatted as '(NaN)'</li>
196         * <li>Positive infinity is formatted as '(Infinity)'</li>
197         * <li>Negative infinity is formatted as '(-Infinity)'</li>
198         * </ol>
199         *
200         * @param value the double to format.
201         * @param format the format used.
202         * @param toAppendTo where the text is to be appended
203         * @param pos On input: an alignment field, if desired. On output: the
204         *            offsets of the alignment field
205         * @return the value passed in as toAppendTo.
206         */
207        protected StringBuffer formatDouble(final double value, final NumberFormat format,
208                                            final StringBuffer toAppendTo,
209                                            final FieldPosition pos) {
210            if( Double.isNaN(value) || Double.isInfinite(value) ) {
211                toAppendTo.append('(');
212                toAppendTo.append(value);
213                toAppendTo.append(')');
214            } else {
215                format.format(value, toAppendTo, pos);
216            }
217            return toAppendTo;
218        }
219    
220    }