CompositeFormat.java

  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.math4.legacy.util;

  18. import java.text.FieldPosition;
  19. import java.text.NumberFormat;
  20. import java.text.ParsePosition;
  21. import java.util.Locale;

  22. /**
  23.  * Base class for formatters of composite objects (complex numbers, vectors ...).
  24.  *
  25.  */
  26. public final class CompositeFormat {

  27.     /**
  28.      * Class contains only static methods.
  29.      */
  30.     private CompositeFormat() {}

  31.     /**
  32.      * Create a default number format.  The default number format is based on
  33.      * {@link NumberFormat#getInstance()} with the only customizing that the
  34.      * maximum number of fraction digits is set to 10.
  35.      * @return the default number format.
  36.      */
  37.     public static NumberFormat getDefaultNumberFormat() {
  38.         return getDefaultNumberFormat(Locale.getDefault());
  39.     }

  40.     /**
  41.      * Create a default number format.  The default number format is based on
  42.      * {@link NumberFormat#getInstance(java.util.Locale)} with the only
  43.      * customizing that the maximum number of fraction digits is set to 10.
  44.      * @param locale the specific locale used by the format.
  45.      * @return the default number format specific to the given locale.
  46.      */
  47.     public static NumberFormat getDefaultNumberFormat(final Locale locale) {
  48.         final NumberFormat nf = NumberFormat.getInstance(locale);
  49.         nf.setMaximumFractionDigits(10);
  50.         return nf;
  51.     }

  52.     /**
  53.      * Parses <code>source</code> until a non-whitespace character is found.
  54.      *
  55.      * @param source the string to parse
  56.      * @param pos input/output parsing parameter.  On output, <code>pos</code>
  57.      *        holds the index of the next non-whitespace character.
  58.      */
  59.     public static void parseAndIgnoreWhitespace(final String source,
  60.                                                 final ParsePosition pos) {
  61.         parseNextCharacter(source, pos);
  62.         pos.setIndex(pos.getIndex() - 1);
  63.     }

  64.     /**
  65.      * Parses <code>source</code> until a non-whitespace character is found.
  66.      *
  67.      * @param source the string to parse
  68.      * @param pos input/output parsing parameter.
  69.      * @return the first non-whitespace character.
  70.      */
  71.     public static char parseNextCharacter(final String source,
  72.                                           final ParsePosition pos) {
  73.          int index = pos.getIndex();
  74.          final int n = source.length();
  75.          char ret = 0;

  76.          if (index < n) {
  77.              char c;
  78.              do {
  79.                  c = source.charAt(index++);
  80.              } while (Character.isWhitespace(c) && index < n);
  81.              pos.setIndex(index);

  82.              if (index < n) {
  83.                  ret = c;
  84.              }
  85.          }

  86.          return ret;
  87.     }

  88.     /**
  89.      * Parses <code>source</code> for special double values.  These values
  90.      * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
  91.      *
  92.      * @param source the string to parse
  93.      * @param value the special value to parse.
  94.      * @param pos input/output parsing parameter.
  95.      * @return the special number.
  96.      */
  97.     private static Number parseNumber(final String source, final double value,
  98.                                       final ParsePosition pos) {
  99.         Number ret = null;

  100.         StringBuilder sb = new StringBuilder();
  101.         sb.append('(');
  102.         sb.append(value);
  103.         sb.append(')');

  104.         final int n = sb.length();
  105.         final int startIndex = pos.getIndex();
  106.         final int endIndex = startIndex + n;
  107.         if (endIndex < source.length() &&
  108.             source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
  109.             ret = Double.valueOf(value);
  110.             pos.setIndex(endIndex);
  111.         }

  112.         return ret;
  113.     }

  114.     /**
  115.      * Parses <code>source</code> for a number.  This method can parse normal,
  116.      * numeric values as well as special values.  These special values include
  117.      * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
  118.      *
  119.      * @param source the string to parse
  120.      * @param format the number format used to parse normal, numeric values.
  121.      * @param pos input/output parsing parameter.
  122.      * @return the parsed number.
  123.      */
  124.     public static Number parseNumber(final String source, final NumberFormat format,
  125.                                      final ParsePosition pos) {
  126.         final int startIndex = pos.getIndex();
  127.         Number number = format.parse(source, pos);
  128.         final int endIndex = pos.getIndex();

  129.         // check for error parsing number
  130.         if (startIndex == endIndex) {
  131.             // try parsing special numbers
  132.             final double[] special = {
  133.                 Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
  134.             };
  135.             for (int i = 0; i < special.length; ++i) {
  136.                 number = parseNumber(source, special[i], pos);
  137.                 if (number != null) {
  138.                     break;
  139.                 }
  140.             }
  141.         }

  142.         return number;
  143.     }

  144.     /**
  145.      * Parse <code>source</code> for an expected fixed string.
  146.      * @param source the string to parse
  147.      * @param expected expected string
  148.      * @param pos input/output parsing parameter.
  149.      * @return true if the expected string was there
  150.      */
  151.     public static boolean parseFixedstring(final String source,
  152.                                            final String expected,
  153.                                            final ParsePosition pos) {

  154.         final int startIndex = pos.getIndex();
  155.         final int endIndex = startIndex + expected.length();
  156.         if (startIndex >= source.length() ||
  157.             endIndex > source.length() ||
  158.             source.substring(startIndex, endIndex).compareTo(expected) != 0) {
  159.             // set index back to start, error index should be the start index
  160.             pos.setIndex(startIndex);
  161.             pos.setErrorIndex(startIndex);
  162.             return false;
  163.         }

  164.         // the string was here
  165.         pos.setIndex(endIndex);
  166.         return true;
  167.     }

  168.     /**
  169.      * Formats a double value to produce a string.  In general, the value is
  170.      * formatted using the formatting rules of <code>format</code>.  There are
  171.      * three exceptions to this:
  172.      * <ol>
  173.      * <li>NaN is formatted as '(NaN)'</li>
  174.      * <li>Positive infinity is formatted as '(Infinity)'</li>
  175.      * <li>Negative infinity is formatted as '(-Infinity)'</li>
  176.      * </ol>
  177.      *
  178.      * @param value the double to format.
  179.      * @param format the format used.
  180.      * @param toAppendTo where the text is to be appended
  181.      * @param pos On input: an alignment field, if desired. On output: the
  182.      *            offsets of the alignment field
  183.      * @return the value passed in as toAppendTo.
  184.      */
  185.     public static StringBuffer formatDouble(final double value, final NumberFormat format,
  186.                                             final StringBuffer toAppendTo,
  187.                                             final FieldPosition pos) {
  188.         if( Double.isNaN(value) || Double.isInfinite(value) ) {
  189.             toAppendTo.append('(');
  190.             toAppendTo.append(value);
  191.             toAppendTo.append(')');
  192.         } else {
  193.             format.format(value, toAppendTo, pos);
  194.         }
  195.         return toAppendTo;
  196.     }
  197. }