extracted a CompositeFormat base class from ComplexFormat

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_0@680026 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2008-07-26 18:28:45 +00:00
parent 51a7a27740
commit 7004e490aa
3 changed files with 251 additions and 194 deletions

View File

@ -17,28 +17,28 @@
package org.apache.commons.math.complex;
import java.io.Serializable;
import java.text.FieldPosition;
import java.text.Format;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
import org.apache.commons.math.util.CompositeFormat;
/**
* Formats a Complex number in cartesian format "Re(c) + Im(c)i". 'i' can
* be replaced with 'j', and the number format for both real and imaginary parts
* can be configured.
* be replaced with 'j' (or anything else), and the number format for both real
* and imaginary parts can be configured.
*
* @author Apache Software Foundation
* @version $Revision$ $Date$
*/
public class ComplexFormat extends Format implements Serializable {
public class ComplexFormat extends CompositeFormat {
/** Serializable version identifier */
private static final long serialVersionUID = -6337346779577272306L;
/** The default imaginary character. */
private static final long serialVersionUID = -3343698360149467646L;
/** The default imaginary character. */
private static final String DEFAULT_IMAGINARY_CHARACTER = "i";
/** The notation used to signify the imaginary part of the complex number. */
@ -73,8 +73,7 @@ public class ComplexFormat extends Format implements Serializable {
* @param realFormat the custom format for the real part.
* @param imaginaryFormat the custom format for the imaginary part.
*/
public ComplexFormat(NumberFormat realFormat,
NumberFormat imaginaryFormat) {
public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat) {
this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat);
}
@ -114,14 +113,23 @@ public class ComplexFormat extends Format implements Serializable {
}
/**
* This static method calls formatComplex() on a default instance of
* Get the set of locales for which complex formats are available.
* <p>This is the same set as the {@link NumberFormat} set.</p>
* @return available complex format locales.
*/
public static Locale[] getAvailableLocales() {
return NumberFormat.getAvailableLocales();
}
/**
* This static method calls {@link #format(Object)} on a default instance of
* ComplexFormat.
*
* @param c Complex object to format
* @return A formatted number in the form "Re(c) + Im(c)i"
*/
public static String formatComplex( Complex c ) {
return getInstance().format( c );
public static String formatComplex(Complex c) {
return getInstance().format(c);
}
/**
@ -182,74 +190,12 @@ public class ComplexFormat extends Format implements Serializable {
ret = format( new Complex(((Number)obj).doubleValue(), 0.0),
toAppendTo, pos);
} else {
throw new IllegalArgumentException(
"Cannot format given Object as a Date");
throw new IllegalArgumentException("Cannot format given Object as a Complex");
}
return ret;
}
/**
* 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.
*/
private StringBuffer formatDouble(double value, NumberFormat format,
StringBuffer toAppendTo, FieldPosition pos) {
if( Double.isNaN(value) || Double.isInfinite(value) ) {
toAppendTo.append('(');
toAppendTo.append(value);
toAppendTo.append(')');
} else {
format.format(value, toAppendTo, pos);
}
return toAppendTo;
}
/**
* Get the set of locales for which complex formats are available. This
* is the same set as the {@link NumberFormat} set.
* @return available complex format locales.
*/
public static Locale[] getAvailableLocales() {
return NumberFormat.getAvailableLocales();
}
/**
* Create a default number format. The default number format is based on
* {@link NumberFormat#getInstance()} with the only customizing is the
* maximum number of fraction digits, which is set to 2.
* @return the default number format.
*/
private 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 is the maximum number of fraction digits, which is set to 2.
* @param locale the specific locale used by the format.
* @return the default number format specific to the given locale.
*/
private static NumberFormat getDefaultNumberFormat(Locale locale) {
NumberFormat nf = NumberFormat.getInstance(locale);
nf.setMaximumFractionDigits(2);
return nf;
}
/**
* Access the imaginaryCharacter.
* @return the imaginaryCharacter.
@ -270,7 +216,7 @@ public class ComplexFormat extends Format implements Serializable {
* Returns the default complex format for the current locale.
* @return the default complex format.
*/
public static ComplexFormat getInstance() {
public static CompositeFormat getInstance() {
return getInstance(Locale.getDefault());
}
@ -305,7 +251,7 @@ public class ComplexFormat extends Format implements Serializable {
Complex result = parse(source, parsePosition);
if (parsePosition.getIndex() == 0) {
throw new ParseException("Unparseable complex number: \"" + source +
"\"", parsePosition.getErrorIndex());
"\"", parsePosition.getErrorIndex());
}
return result;
}
@ -328,7 +274,6 @@ public class ComplexFormat extends Format implements Serializable {
if (re == null) {
// invalid real number
// set index back to initial, error index should already be set
// character examined.
pos.setIndex(initialIndex);
return null;
}
@ -365,129 +310,19 @@ public class ComplexFormat extends Format implements Serializable {
if (im == null) {
// invalid imaginary number
// set index back to initial, error index should already be set
// character examined.
pos.setIndex(initialIndex);
return null;
}
// parse imaginary character
int n = getImaginaryCharacter().length();
startIndex = pos.getIndex();
int endIndex = startIndex + n;
if ((startIndex >= source.length()) ||
(endIndex > source.length()) ||
source.substring(startIndex, endIndex).compareTo(
getImaginaryCharacter()) != 0) {
// set index back to initial, error index should be the start index
// character examined.
pos.setIndex(initialIndex);
pos.setErrorIndex(startIndex);
if (!parseFixedstring(source, getImaginaryCharacter(), pos)) {
return null;
}
pos.setIndex(endIndex);
return new Complex(re.doubleValue(), im.doubleValue() * sign);
}
/**
* 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.
*/
private void parseAndIgnoreWhitespace(String source, 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.
*/
private char parseNextCharacter(String source, ParsePosition pos) {
int index = pos.getIndex();
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 a 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(String source, double value, ParsePosition pos) {
Number ret = null;
StringBuffer sb = new StringBuffer();
sb.append('(');
sb.append(value);
sb.append(')');
int n = sb.length();
int startIndex = pos.getIndex();
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.
*/
private Number parseNumber(String source, NumberFormat format, ParsePosition pos) {
int startIndex = pos.getIndex();
Number number = format.parse(source, pos);
int endIndex = pos.getIndex();
// check for error parsing number
if (startIndex == endIndex) {
// try parsing special numbers
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;
}
/**
* Parses a string to produce a object.
*
@ -499,6 +334,7 @@ public class ComplexFormat extends Format implements Serializable {
public Object parseObject(String source, ParsePosition pos) {
return parse(source, pos);
}
/**
* Modify the imaginaryCharacter.
* @param imaginaryCharacter The new imaginaryCharacter value.
@ -540,4 +376,5 @@ public class ComplexFormat extends Format implements Serializable {
}
this.realFormat = realFormat;
}
}

View File

@ -0,0 +1,218 @@
/*
* 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 ...).
*
* @author Apache Software Foundation
* @version $Revision$ $Date$
*/
public abstract class CompositeFormat extends Format {
/**
* Create a default number format. The default number format is based on
* {@link NumberFormat#getInstance()} with the only customizing is the
* maximum number of fraction digits, which 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 is the maximum number of fraction digits, which 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 a 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;
StringBuffer sb = new StringBuffer();
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;
}
}

View File

@ -22,11 +22,13 @@ import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
import org.apache.commons.math.util.CompositeFormat;
import junit.framework.TestCase;
public abstract class ComplexFormatAbstractTest extends TestCase {
ComplexFormat complexFormat = null;
CompositeFormat complexFormat = null;
ComplexFormat complexFormatJ = null;
protected abstract Locale getLocale();
@ -333,7 +335,7 @@ public abstract class ComplexFormatAbstractTest extends TestCase {
}
public void testFormatNumber() {
ComplexFormat cf = ComplexFormat.getInstance(getLocale());
CompositeFormat cf = ComplexFormat.getInstance(getLocale());
Double pi = Double.valueOf(Math.PI);
String text = cf.format(pi);
assertEquals("3" + getDecimalCharacter() + "14", text);
@ -341,7 +343,7 @@ public abstract class ComplexFormatAbstractTest extends TestCase {
public void testFormatObject() {
try {
ComplexFormat cf = new ComplexFormat();
CompositeFormat cf = new ComplexFormat();
Object object = new Object();
cf.format(object);
fail();