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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.text.FieldPosition;
  23. import java.text.NumberFormat;
  24. import java.text.ParsePosition;
  25. import java.util.Locale;

  26. /**
  27.  * Base class for formatters of composite objects (complex numbers, vectors ...).
  28.  */
  29. public class CompositeFormat {

  30.     /**
  31.      * Class contains only static methods.
  32.      */
  33.     private CompositeFormat() {}

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

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

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

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

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

  85.              if (index < n) {
  86.                  ret = c;
  87.              }
  88.          }

  89.          return ret;
  90.     }

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

  103.         StringBuilder sb = new StringBuilder();
  104.         sb.append('(').append(value).append(')');

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

  113.         return ret;
  114.     }

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

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

  143.         return number;
  144.     }

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

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

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

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