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:
parent
51a7a27740
commit
7004e490aa
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue