    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    }