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