Home | History | Annotate | Download | only in util
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 package org.apache.commons.math.util;
     18 
     19 import java.text.FieldPosition;
     20 import java.text.Format;
     21 import java.text.NumberFormat;
     22 import java.text.ParsePosition;
     23 import java.util.Locale;
     24 
     25 /**
     26  * Base class for formatters of composite objects (complex numbers, vectors ...).
     27  *
     28  * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 dc. 2010) $
     29  */
     30 public abstract class CompositeFormat extends Format {
     31 
     32     /** Serializable version identifier. */
     33     private static final long serialVersionUID = 5358685519349262494L;
     34 
     35     /**
     36      * Create a default number format.  The default number format is based on
     37      * {@link NumberFormat#getInstance()} with the only customizing that the
     38      * maximum number of fraction digits is set to 2.
     39      * @return the default number format.
     40      */
     41     protected static NumberFormat getDefaultNumberFormat() {
     42         return getDefaultNumberFormat(Locale.getDefault());
     43     }
     44 
     45     /**
     46      * Create a default number format.  The default number format is based on
     47      * {@link NumberFormat#getInstance(java.util.Locale)} with the only
     48      * customizing that the maximum number of fraction digits is set to 2.
     49      * @param locale the specific locale used by the format.
     50      * @return the default number format specific to the given locale.
     51      */
     52     protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
     53         final NumberFormat nf = NumberFormat.getInstance(locale);
     54         nf.setMaximumFractionDigits(2);
     55         return nf;
     56     }
     57 
     58     /**
     59      * Parses <code>source</code> until a non-whitespace character is found.
     60      *
     61      * @param source the string to parse
     62      * @param pos input/ouput parsing parameter.  On output, <code>pos</code>
     63      *        holds the index of the next non-whitespace character.
     64      */
     65     protected void parseAndIgnoreWhitespace(final String source,
     66                                             final ParsePosition pos) {
     67         parseNextCharacter(source, pos);
     68         pos.setIndex(pos.getIndex() - 1);
     69     }
     70 
     71     /**
     72      * Parses <code>source</code> until a non-whitespace character is found.
     73      *
     74      * @param source the string to parse
     75      * @param pos input/ouput parsing parameter.
     76      * @return the first non-whitespace character.
     77      */
     78     protected char parseNextCharacter(final String source,
     79                                       final ParsePosition pos) {
     80          int index = pos.getIndex();
     81          final int n = source.length();
     82          char ret = 0;
     83 
     84          if (index < n) {
     85              char c;
     86              do {
     87                  c = source.charAt(index++);
     88              } while (Character.isWhitespace(c) && index < n);
     89              pos.setIndex(index);
     90 
     91              if (index < n) {
     92                  ret = c;
     93              }
     94          }
     95 
     96          return ret;
     97     }
     98 
     99     /**
    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 }
    221