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.math.util;
018
019 import java.text.FieldPosition;
020 import java.text.Format;
021 import java.text.NumberFormat;
022 import java.text.ParsePosition;
023 import java.util.Locale;
024
025 /**
026 * Base class for formatters of composite objects (complex numbers, vectors ...).
027 *
028 * @version $Revision: 811783 $ $Date: 2009-09-06 04:56:58 -0400 (Sun, 06 Sep 2009) $
029 */
030 public abstract class CompositeFormat extends Format {
031
032 /** Serializable version identifier. */
033 private static final long serialVersionUID = 5358685519349262494L;
034
035 /**
036 * Create a default number format. The default number format is based on
037 * {@link NumberFormat#getInstance()} with the only customizing that the
038 * maximum number of fraction digits is set to 2.
039 * @return the default number format.
040 */
041 protected static NumberFormat getDefaultNumberFormat() {
042 return getDefaultNumberFormat(Locale.getDefault());
043 }
044
045 /**
046 * Create a default number format. The default number format is based on
047 * {@link NumberFormat#getInstance(java.util.Locale)} with the only
048 * customizing that the maximum number of fraction digits is set to 2.
049 * @param locale the specific locale used by the format.
050 * @return the default number format specific to the given locale.
051 */
052 protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
053 final NumberFormat nf = NumberFormat.getInstance(locale);
054 nf.setMaximumFractionDigits(2);
055 return nf;
056 }
057
058 /**
059 * Parses <code>source</code> until a non-whitespace character is found.
060 *
061 * @param source the string to parse
062 * @param pos input/ouput parsing parameter. On output, <code>pos</code>
063 * holds the index of the next non-whitespace character.
064 */
065 protected void parseAndIgnoreWhitespace(final String source,
066 final ParsePosition pos) {
067 parseNextCharacter(source, pos);
068 pos.setIndex(pos.getIndex() - 1);
069 }
070
071 /**
072 * Parses <code>source</code> until a non-whitespace character is found.
073 *
074 * @param source the string to parse
075 * @param pos input/ouput parsing parameter.
076 * @return the first non-whitespace character.
077 */
078 protected char parseNextCharacter(final String source,
079 final ParsePosition pos) {
080 int index = pos.getIndex();
081 final int n = source.length();
082 char ret = 0;
083
084 if (index < n) {
085 char c;
086 do {
087 c = source.charAt(index++);
088 } while (Character.isWhitespace(c) && index < n);
089 pos.setIndex(index);
090
091 if (index < n) {
092 ret = c;
093 }
094 }
095
096 return ret;
097 }
098
099 /**
100 * Parses <code>source</code> for special double values. These values
101 * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
102 *
103 * @param source the string to parse
104 * @param value the special value to parse.
105 * @param pos input/ouput parsing parameter.
106 * @return the special number.
107 */
108 private Number parseNumber(final String source, final double value,
109 final ParsePosition pos) {
110 Number ret = null;
111
112 StringBuffer sb = new StringBuffer();
113 sb.append('(');
114 sb.append(value);
115 sb.append(')');
116
117 final int n = sb.length();
118 final int startIndex = pos.getIndex();
119 final int endIndex = startIndex + n;
120 if (endIndex < source.length()) {
121 if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
122 ret = Double.valueOf(value);
123 pos.setIndex(endIndex);
124 }
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/ouput parsing parameter.
138 * @return the parsed number.
139 */
140 protected 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/ouput parsing parameter.
168 * @return true if the expected string was there
169 */
170 protected boolean parseFixedstring(final String source, final String expected,
171 final ParsePosition pos) {
172
173 final int startIndex = pos.getIndex();
174 final int endIndex = startIndex + expected.length();
175 if ((startIndex >= source.length()) ||
176 (endIndex > source.length()) ||
177 (source.substring(startIndex, endIndex).compareTo(expected) != 0)) {
178 // set index back to start, error index should be the start index
179 pos.setIndex(startIndex);
180 pos.setErrorIndex(startIndex);
181 return false;
182 }
183
184 // the string was here
185 pos.setIndex(endIndex);
186 return true;
187
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 protected 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
220 }