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 = 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 (double v : special) {
154 number = parseNumber(source, v, 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('(').append(value).append(')');
213 } else {
214 format.format(value, toAppendTo, pos);
215 }
216 return toAppendTo;
217 }
218 }