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.math3.util;
18
19 import java.text.FieldPosition;
20 import java.text.NumberFormat;
21 import java.text.ParsePosition;
22 import java.util.Locale;
23
24 /**
25 * Base class for formatters of composite objects (complex numbers, vectors ...).
26 *
27 * @version $Id: CompositeFormat.java 1462503 2013-03-29 15:48:27Z luc $
28 */
29 public class CompositeFormat {
30
31 /**
32 * Class contains only static methods.
33 */
34 private CompositeFormat() {}
35
36 /**
37 * Create a default number format. The default number format is based on
38 * {@link NumberFormat#getInstance()} with the only customizing that the
39 * maximum number of fraction digits is set to 10.
40 * @return the default number format.
41 */
42 public static NumberFormat getDefaultNumberFormat() {
43 return getDefaultNumberFormat(Locale.getDefault());
44 }
45
46 /**
47 * Create a default number format. The default number format is based on
48 * {@link NumberFormat#getInstance(java.util.Locale)} with the only
49 * customizing that the maximum number of fraction digits is set to 10.
50 * @param locale the specific locale used by the format.
51 * @return the default number format specific to the given locale.
52 */
53 public static NumberFormat getDefaultNumberFormat(final Locale locale) {
54 final NumberFormat nf = NumberFormat.getInstance(locale);
55 nf.setMaximumFractionDigits(10);
56 return nf;
57 }
58
59 /**
60 * Parses <code>source</code> until a non-whitespace character is found.
61 *
62 * @param source the string to parse
63 * @param pos input/output parsing parameter. On output, <code>pos</code>
64 * holds the index of the next non-whitespace character.
65 */
66 public static void parseAndIgnoreWhitespace(final String source,
67 final ParsePosition pos) {
68 parseNextCharacter(source, pos);
69 pos.setIndex(pos.getIndex() - 1);
70 }
71
72 /**
73 * Parses <code>source</code> until a non-whitespace character is found.
74 *
75 * @param source the string to parse
76 * @param pos input/output parsing parameter.
77 * @return the first non-whitespace character.
78 */
79 public static char parseNextCharacter(final String source,
80 final ParsePosition pos) {
81 int index = pos.getIndex();
82 final int n = source.length();
83 char ret = 0;
84
85 if (index < n) {
86 char c;
87 do {
88 c = source.charAt(index++);
89 } while (Character.isWhitespace(c) && index < n);
90 pos.setIndex(index);
91
92 if (index < n) {
93 ret = c;
94 }
95 }
96
97 return ret;
98 }
99
100 /**
101 * Parses <code>source</code> for special double values. These values
102 * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
103 *
104 * @param source the string to parse
105 * @param value the special value to parse.
106 * @param pos input/output parsing parameter.
107 * @return the special number.
108 */
109 private static Number parseNumber(final String source, final double value,
110 final ParsePosition pos) {
111 Number ret = null;
112
113 StringBuilder sb = new StringBuilder();
114 sb.append('(');
115 sb.append(value);
116 sb.append(')');
117
118 final int n = sb.length();
119 final int startIndex = pos.getIndex();
120 final int endIndex = startIndex + n;
121 if (endIndex < source.length() &&
122 source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
123 ret = Double.valueOf(value);
124 pos.setIndex(endIndex);
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/output parsing parameter.
138 * @return the parsed number.
139 */
140 public static 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/output parsing parameter.
168 * @return true if the expected string was there
169 */
170 public static boolean parseFixedstring(final String source,
171 final String expected,
172 final ParsePosition pos) {
173
174 final int startIndex = pos.getIndex();
175 final int endIndex = startIndex + expected.length();
176 if ((startIndex >= source.length()) ||
177 (endIndex > source.length()) ||
178 (source.substring(startIndex, endIndex).compareTo(expected) != 0)) {
179 // set index back to start, error index should be the start index
180 pos.setIndex(startIndex);
181 pos.setErrorIndex(startIndex);
182 return false;
183 }
184
185 // the string was here
186 pos.setIndex(endIndex);
187 return true;
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 public static 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 }