| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.math.util; |
| |
| import java.text.FieldPosition; |
| import java.text.Format; |
| import java.text.NumberFormat; |
| import java.text.ParsePosition; |
| import java.util.Locale; |
| |
| /** |
| * Base class for formatters of composite objects (complex numbers, vectors ...). |
| * |
| * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $ |
| */ |
| public abstract class CompositeFormat extends Format { |
| |
| /** Serializable version identifier. */ |
| private static final long serialVersionUID = 5358685519349262494L; |
| |
| /** |
| * Create a default number format. The default number format is based on |
| * {@link NumberFormat#getInstance()} with the only customizing that the |
| * maximum number of fraction digits is set to 2. |
| * @return the default number format. |
| */ |
| protected static NumberFormat getDefaultNumberFormat() { |
| return getDefaultNumberFormat(Locale.getDefault()); |
| } |
| |
| /** |
| * Create a default number format. The default number format is based on |
| * {@link NumberFormat#getInstance(java.util.Locale)} with the only |
| * customizing that the maximum number of fraction digits is set to 2. |
| * @param locale the specific locale used by the format. |
| * @return the default number format specific to the given locale. |
| */ |
| protected static NumberFormat getDefaultNumberFormat(final Locale locale) { |
| final NumberFormat nf = NumberFormat.getInstance(locale); |
| nf.setMaximumFractionDigits(2); |
| return nf; |
| } |
| |
| /** |
| * Parses <code>source</code> until a non-whitespace character is found. |
| * |
| * @param source the string to parse |
| * @param pos input/ouput parsing parameter. On output, <code>pos</code> |
| * holds the index of the next non-whitespace character. |
| */ |
| protected void parseAndIgnoreWhitespace(final String source, |
| final ParsePosition pos) { |
| parseNextCharacter(source, pos); |
| pos.setIndex(pos.getIndex() - 1); |
| } |
| |
| /** |
| * Parses <code>source</code> until a non-whitespace character is found. |
| * |
| * @param source the string to parse |
| * @param pos input/ouput parsing parameter. |
| * @return the first non-whitespace character. |
| */ |
| protected char parseNextCharacter(final String source, |
| final ParsePosition pos) { |
| int index = pos.getIndex(); |
| final int n = source.length(); |
| char ret = 0; |
| |
| if (index < n) { |
| char c; |
| do { |
| c = source.charAt(index++); |
| } while (Character.isWhitespace(c) && index < n); |
| pos.setIndex(index); |
| |
| if (index < n) { |
| ret = c; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Parses <code>source</code> for special double values. These values |
| * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. |
| * |
| * @param source the string to parse |
| * @param value the special value to parse. |
| * @param pos input/ouput parsing parameter. |
| * @return the special number. |
| */ |
| private Number parseNumber(final String source, final double value, |
| final ParsePosition pos) { |
| Number ret = null; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append('('); |
| sb.append(value); |
| sb.append(')'); |
| |
| final int n = sb.length(); |
| final int startIndex = pos.getIndex(); |
| final int endIndex = startIndex + n; |
| if (endIndex < source.length()) { |
| if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) { |
| ret = Double.valueOf(value); |
| pos.setIndex(endIndex); |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Parses <code>source</code> for a number. This method can parse normal, |
| * numeric values as well as special values. These special values include |
| * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. |
| * |
| * @param source the string to parse |
| * @param format the number format used to parse normal, numeric values. |
| * @param pos input/ouput parsing parameter. |
| * @return the parsed number. |
| */ |
| protected Number parseNumber(final String source, final NumberFormat format, |
| final ParsePosition pos) { |
| final int startIndex = pos.getIndex(); |
| Number number = format.parse(source, pos); |
| final int endIndex = pos.getIndex(); |
| |
| // check for error parsing number |
| if (startIndex == endIndex) { |
| // try parsing special numbers |
| final double[] special = { |
| Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY |
| }; |
| for (int i = 0; i < special.length; ++i) { |
| number = parseNumber(source, special[i], pos); |
| if (number != null) { |
| break; |
| } |
| } |
| } |
| |
| return number; |
| } |
| |
| /** |
| * Parse <code>source</code> for an expected fixed string. |
| * @param source the string to parse |
| * @param expected expected string |
| * @param pos input/ouput parsing parameter. |
| * @return true if the expected string was there |
| */ |
| protected boolean parseFixedstring(final String source, final String expected, |
| final ParsePosition pos) { |
| |
| final int startIndex = pos.getIndex(); |
| final int endIndex = startIndex + expected.length(); |
| if ((startIndex >= source.length()) || |
| (endIndex > source.length()) || |
| (source.substring(startIndex, endIndex).compareTo(expected) != 0)) { |
| // set index back to start, error index should be the start index |
| pos.setIndex(startIndex); |
| pos.setErrorIndex(startIndex); |
| return false; |
| } |
| |
| // the string was here |
| pos.setIndex(endIndex); |
| return true; |
| |
| } |
| |
| /** |
| * Formats a double value to produce a string. In general, the value is |
| * formatted using the formatting rules of <code>format</code>. There are |
| * three exceptions to this: |
| * <ol> |
| * <li>NaN is formatted as '(NaN)'</li> |
| * <li>Positive infinity is formatted as '(Infinity)'</li> |
| * <li>Negative infinity is formatted as '(-Infinity)'</li> |
| * </ol> |
| * |
| * @param value the double to format. |
| * @param format the format used. |
| * @param toAppendTo where the text is to be appended |
| * @param pos On input: an alignment field, if desired. On output: the |
| * offsets of the alignment field |
| * @return the value passed in as toAppendTo. |
| */ |
| protected StringBuffer formatDouble(final double value, final NumberFormat format, |
| final StringBuffer toAppendTo, |
| final FieldPosition pos) { |
| if( Double.isNaN(value) || Double.isInfinite(value) ) { |
| toAppendTo.append('('); |
| toAppendTo.append(value); |
| toAppendTo.append(')'); |
| } else { |
| format.format(value, toAppendTo, pos); |
| } |
| return toAppendTo; |
| } |
| |
| } |