View Javadoc
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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.util;
23  
24  import java.text.FieldPosition;
25  import java.text.NumberFormat;
26  import java.text.ParsePosition;
27  import java.util.Locale;
28  
29  /**
30   * Base class for formatters of composite objects (complex numbers, vectors ...).
31   */
32  public class CompositeFormat {
33  
34      /**
35       * Class contains only static methods.
36       */
37      private CompositeFormat() {}
38  
39      /**
40       * Create a default number format.  The default number format is based on
41       * {@link NumberFormat#getInstance()} with the only customizing that the
42       * maximum number of fraction digits is set to 10.
43       * @return the default number format.
44       */
45      public static NumberFormat getDefaultNumberFormat() {
46          return getDefaultNumberFormat(Locale.getDefault());
47      }
48  
49      /**
50       * Create a default number format.  The default number format is based on
51       * {@link NumberFormat#getInstance(java.util.Locale)} with the only
52       * customizing that the maximum number of fraction digits is set to 10.
53       * @param locale the specific locale used by the format.
54       * @return the default number format specific to the given locale.
55       */
56      public static NumberFormat getDefaultNumberFormat(final Locale locale) {
57          final NumberFormat nf = NumberFormat.getInstance(locale);
58          nf.setMaximumFractionDigits(10);
59          return nf;
60      }
61  
62      /**
63       * Parses <code>source</code> until a non-whitespace character is found.
64       *
65       * @param source the string to parse
66       * @param pos input/output parsing parameter.  On output, <code>pos</code>
67       *        holds the index of the next non-whitespace character.
68       */
69      public static void parseAndIgnoreWhitespace(final String source,
70                                                  final ParsePosition pos) {
71          parseNextCharacter(source, pos);
72          pos.setIndex(pos.getIndex() - 1);
73      }
74  
75      /**
76       * Parses <code>source</code> until a non-whitespace character is found.
77       *
78       * @param source the string to parse
79       * @param pos input/output parsing parameter.
80       * @return the first non-whitespace character.
81       */
82      public static char parseNextCharacter(final String source,
83                                            final ParsePosition pos) {
84           int index = pos.getIndex();
85           final int n = source.length();
86           char ret = 0;
87  
88           if (index < n) {
89               char c;
90               do {
91                   c = source.charAt(index++);
92               } while (Character.isWhitespace(c) && index < n);
93               pos.setIndex(index);
94  
95               if (index < n) {
96                   ret = c;
97               }
98           }
99  
100          return ret;
101     }
102 
103     /**
104      * Parses <code>source</code> for special double values.  These values
105      * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
106      *
107      * @param source the string to parse
108      * @param value the special value to parse.
109      * @param pos input/output parsing parameter.
110      * @return the special number.
111      */
112     private static Number parseNumber(final String source, final double value,
113                                       final ParsePosition pos) {
114         Number ret = null;
115 
116         StringBuilder sb = new StringBuilder();
117         sb.append('(').append(value).append(')');
118 
119         final int n = sb.length();
120         final int startIndex = pos.getIndex();
121         final int endIndex = startIndex + n;
122         if (endIndex < source.length() &&
123             source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
124             ret = Double.valueOf(value);
125             pos.setIndex(endIndex);
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 }