001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math3.util;
018
019 import java.text.FieldPosition;
020 import java.text.NumberFormat;
021 import java.text.ParsePosition;
022 import java.util.Locale;
023
024 /**
025 * Base class for formatters of composite objects (complex numbers, vectors ...).
026 *
027 * @version $Id: CompositeFormat.java 1416643 2012-12-03 19:37:14Z tn $
028 */
029 public class CompositeFormat {
030
031 /**
032 * Class contains only static methods.
033 */
034 private CompositeFormat() {}
035
036 /**
037 * Create a default number format. The default number format is based on
038 * {@link NumberFormat#getInstance()} with the only customizing that the
039 * maximum number of fraction digits is set to 10.
040 * @return the default number format.
041 */
042 public static NumberFormat getDefaultNumberFormat() {
043 return getDefaultNumberFormat(Locale.getDefault());
044 }
045
046 /**
047 * Create a default number format. The default number format is based on
048 * {@link NumberFormat#getInstance(java.util.Locale)} with the only
049 * customizing that the maximum number of fraction digits is set to 10.
050 * @param locale the specific locale used by the format.
051 * @return the default number format specific to the given locale.
052 */
053 public static NumberFormat getDefaultNumberFormat(final Locale locale) {
054 final NumberFormat nf = NumberFormat.getInstance(locale);
055 nf.setMaximumFractionDigits(10);
056 return nf;
057 }
058
059 /**
060 * Parses <code>source</code> until a non-whitespace character is found.
061 *
062 * @param source the string to parse
063 * @param pos input/output parsing parameter. On output, <code>pos</code>
064 * holds the index of the next non-whitespace character.
065 */
066 public static void parseAndIgnoreWhitespace(final String source,
067 final ParsePosition pos) {
068 parseNextCharacter(source, pos);
069 pos.setIndex(pos.getIndex() - 1);
070 }
071
072 /**
073 * Parses <code>source</code> until a non-whitespace character is found.
074 *
075 * @param source the string to parse
076 * @param pos input/output parsing parameter.
077 * @return the first non-whitespace character.
078 */
079 public static char parseNextCharacter(final String source,
080 final ParsePosition pos) {
081 int index = pos.getIndex();
082 final int n = source.length();
083 char ret = 0;
084
085 if (index < n) {
086 char c;
087 do {
088 c = source.charAt(index++);
089 } while (Character.isWhitespace(c) && index < n);
090 pos.setIndex(index);
091
092 if (index < n) {
093 ret = c;
094 }
095 }
096
097 return ret;
098 }
099
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 if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
123 ret = Double.valueOf(value);
124 pos.setIndex(endIndex);
125 }
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 }