    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
    018    package org.apache.commons.math.complex;
    019
    020    import java.text.FieldPosition;
    021    import java.text.NumberFormat;
    022    import java.text.ParseException;
    023    import java.text.ParsePosition;
    024    import java.util.Locale;
    025
    026    import org.apache.commons.math.MathRuntimeException;
    027    import org.apache.commons.math.exception.util.LocalizedFormats;
    028    import org.apache.commons.math.util.CompositeFormat;
    029    import org.apache.commons.math.exception.NullArgumentException;
    030
    031    /**
    032     * Formats a Complex number in cartesian format "Re(c) + Im(c)i".  'i' can
    033     * be replaced with 'j' (or anything else), and the number format for both real
    034     * and imaginary parts can be configured.
    035     *
    036     * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 ao??t 2010) $
    037     */
    038    public class ComplexFormat extends CompositeFormat {
    039
    040        /** Serializable version identifier */
    041        private static final long serialVersionUID = -3343698360149467646L;
    042
    043         /** The default imaginary character. */
    044        private static final String DEFAULT_IMAGINARY_CHARACTER = "i";
    045
    046        /** The notation used to signify the imaginary part of the complex number. */
    047        private String imaginaryCharacter;
    048
    049        /** The format used for the imaginary part. */
    050        private NumberFormat imaginaryFormat;
    051
    052        /** The format used for the real part. */
    053        private NumberFormat realFormat;
    054
    055        /**
    056         * Create an instance with the default imaginary character, 'i', and the
    057         * default number format for both real and imaginary parts.
    058         */
    059        public ComplexFormat() {
    060            this(DEFAULT_IMAGINARY_CHARACTER, getDefaultNumberFormat());
    061        }
    062
    063        /**
    064         * Create an instance with a custom number format for both real and
    065         * imaginary parts.
    066         * @param format the custom format for both real and imaginary parts.
    067         */
    068        public ComplexFormat(NumberFormat format) {
    069            this(DEFAULT_IMAGINARY_CHARACTER, format);
    070        }
    071
    072        /**
    073         * Create an instance with a custom number format for the real part and a
    074         * custom number format for the imaginary part.
    075         * @param realFormat the custom format for the real part.
    076         * @param imaginaryFormat the custom format for the imaginary part.
    077         */
    078        public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat) {
    079            this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat);
    080        }
    081
    082        /**
    083         * Create an instance with a custom imaginary character, and the default
    084         * number format for both real and imaginary parts.
    085         * @param imaginaryCharacter The custom imaginary character.
    086         */
    087        public ComplexFormat(String imaginaryCharacter) {
    088            this(imaginaryCharacter, getDefaultNumberFormat());
    089        }
    090
    091        /**
    092         * Create an instance with a custom imaginary character, and a custom number
    093         * format for both real and imaginary parts.
    094         * @param imaginaryCharacter The custom imaginary character.
    095         * @param format the custom format for both real and imaginary parts.
    096         */
    097        public ComplexFormat(String imaginaryCharacter, NumberFormat format) {
    098            this(imaginaryCharacter, format, (NumberFormat)format.clone());
    099        }
    100
    101        /**
    102         * Create an instance with a custom imaginary character, a custom number
    103         * format for the real part, and a custom number format for the imaginary
    104         * part.
    105         * @param imaginaryCharacter The custom imaginary character.
    106         * @param realFormat the custom format for the real part.
    107         * @param imaginaryFormat the custom format for the imaginary part.
    108         */
    109        public ComplexFormat(String imaginaryCharacter, NumberFormat realFormat,
    110                NumberFormat imaginaryFormat) {
    111            super();
    112            setImaginaryCharacter(imaginaryCharacter);
    113            setImaginaryFormat(imaginaryFormat);
    114            setRealFormat(realFormat);
    115        }
    116
    117        /**
    118         * Get the set of locales for which complex formats are available.
    119         * <p>This is the same set as the {@link NumberFormat} set.</p>
    120         * @return available complex format locales.
    121         */
    122        public static Locale[] getAvailableLocales() {
    123            return NumberFormat.getAvailableLocales();
    124        }
    125
    126        /**
    127         * This static method calls {@link #format(Object)} on a default instance of
    128         * ComplexFormat.
    129         *
    130         * @param c Complex object to format
    131         * @return A formatted number in the form "Re(c) + Im(c)i"
    132         */
    133        public static String formatComplex(Complex c) {
    134            return getInstance().format(c);
    135        }
    136
    137        /**
    138         * Formats a {@link Complex} object to produce a string.
    139         *
    140         * @param complex the object to format.
    141         * @param toAppendTo where the text is to be appended
    142         * @param pos On input: an alignment field, if desired. On output: the
    143         *            offsets of the alignment field
    144         * @return the value passed in as toAppendTo.
    145         */
    146        public StringBuffer format(Complex complex, StringBuffer toAppendTo,
    147                FieldPosition pos) {
    148
    149            pos.setBeginIndex(0);
    150            pos.setEndIndex(0);
    151
    152            // format real
    153            double re = complex.getReal();
    154            formatDouble(re, getRealFormat(), toAppendTo, pos);
    155
    156            // format sign and imaginary
    157            double im = complex.getImaginary();
    158            if (im < 0.0) {
    159                toAppendTo.append(" - ");
    160                formatDouble(-im, getImaginaryFormat(), toAppendTo, pos);
    161                toAppendTo.append(getImaginaryCharacter());
    162            } else if (im > 0.0 || Double.isNaN(im)) {
    163                toAppendTo.append(" + ");
    164                formatDouble(im, getImaginaryFormat(), toAppendTo, pos);
    165                toAppendTo.append(getImaginaryCharacter());
    166            }
    167
    168            return toAppendTo;
    169        }
    170
    171        /**
    172         * Formats a object to produce a string.  <code>obj</code> must be either a
    173         * {@link Complex} object or a {@link Number} object.  Any other type of
    174         * object will result in an {@link IllegalArgumentException} being thrown.
    175         *
    176         * @param obj the object to format.
    177         * @param toAppendTo where the text is to be appended
    178         * @param pos On input: an alignment field, if desired. On output: the
    179         *            offsets of the alignment field
    180         * @return the value passed in as toAppendTo.
    181         * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
    182         * @throws IllegalArgumentException is <code>obj</code> is not a valid type.
    183         */
    184        @Override
    185        public StringBuffer format(Object obj, StringBuffer toAppendTo,
    186                FieldPosition pos) {
    187
    188            StringBuffer ret = null;
    189
    190            if (obj instanceof Complex) {
    191                ret = format( (Complex)obj, toAppendTo, pos);
    192            } else if (obj instanceof Number) {
    193                ret = format( new Complex(((Number)obj).doubleValue(), 0.0),
    194                    toAppendTo, pos);
    195            } else {
    196                throw MathRuntimeException.createIllegalArgumentException(
    197                      LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_COMPLEX,
    198                      obj.getClass().getName());
    199            }
    200
    201            return ret;
    202        }
    203
    204        /**
    205         * Access the imaginaryCharacter.
    206         * @return the imaginaryCharacter.
    207         */
    208        public String getImaginaryCharacter() {
    209            return imaginaryCharacter;
    210        }
    211
    212        /**
    213         * Access the imaginaryFormat.
    214         * @return the imaginaryFormat.
    215         */
    216        public NumberFormat getImaginaryFormat() {
    217            return imaginaryFormat;
    218        }
    219
    220        /**
    221         * Returns the default complex format for the current locale.
    222         * @return the default complex format.
    223         */
    224        public static ComplexFormat getInstance() {
    225            return getInstance(Locale.getDefault());
    226        }
    227
    228        /**
    229         * Returns the default complex format for the given locale.
    230         * @param locale the specific locale used by the format.
    231         * @return the complex format specific to the given locale.
    232         */
    233        public static ComplexFormat getInstance(Locale locale) {
    234            NumberFormat f = getDefaultNumberFormat(locale);
    235            return new ComplexFormat(f);
    236        }
    237
    238        /**
    239         * Access the realFormat.
    240         * @return the realFormat.
    241         */
    242        public NumberFormat getRealFormat() {
    243            return realFormat;
    244        }
    245
    246        /**
    247         * Parses a string to produce a {@link Complex} object.
    248         *
    249         * @param source the string to parse
    250         * @return the parsed {@link Complex} object.
    251         * @exception ParseException if the beginning of the specified string
    252         *            cannot be parsed.
    253         */
    254        public Complex parse(String source) throws ParseException {
    255            ParsePosition parsePosition = new ParsePosition(0);
    256            Complex result = parse(source, parsePosition);
    257            if (parsePosition.getIndex() == 0) {
    258                throw MathRuntimeException.createParseException(
    259                        parsePosition.getErrorIndex(),
    260                        LocalizedFormats.UNPARSEABLE_COMPLEX_NUMBER, source);
    261            }
    262            return result;
    263        }
    264
    265        /**
    266         * Parses a string to produce a {@link Complex} object.
    267         *
    268         * @param source the string to parse
    269         * @param pos input/ouput parsing parameter.
    270         * @return the parsed {@link Complex} object.
    271         */
    272        public Complex parse(String source, ParsePosition pos) {
    273            int initialIndex = pos.getIndex();
    274
    275            // parse whitespace
    276            parseAndIgnoreWhitespace(source, pos);
    277
    278            // parse real
    279            Number re = parseNumber(source, getRealFormat(), pos);
    280            if (re == null) {
    281                // invalid real number
    282                // set index back to initial, error index should already be set
    283                pos.setIndex(initialIndex);
    284                return null;
    285            }
    286
    287            // parse sign
    288            int startIndex = pos.getIndex();
    289            char c = parseNextCharacter(source, pos);
    290            int sign = 0;
    291            switch (c) {
    292            case 0 :
    293                // no sign
    294                // return real only complex number
    295                return new Complex(re.doubleValue(), 0.0);
    296            case '-' :
    297                sign = -1;
    298                break;
    299            case '+' :
    300                sign = 1;
    301                break;
    302            default :
    303                // invalid sign
    304                // set index back to initial, error index should be the last
    305                // character examined.
    306                pos.setIndex(initialIndex);
    307                pos.setErrorIndex(startIndex);
    308                return null;
    309            }
    310
    311            // parse whitespace
    312            parseAndIgnoreWhitespace(source, pos);
    313
    314            // parse imaginary
    315            Number im = parseNumber(source, getRealFormat(), pos);
    316            if (im == null) {
    317                // invalid imaginary number
    318                // set index back to initial, error index should already be set
    319                pos.setIndex(initialIndex);
    320                return null;
    321            }
    322
    323            // parse imaginary character
    324            if (!parseFixedstring(source, getImaginaryCharacter(), pos)) {
    325                return null;
    326            }
    327
    328            return new Complex(re.doubleValue(), im.doubleValue() * sign);
    329
    330        }
    331
    332        /**
    333         * Parses a string to produce a object.
    334         *
    335         * @param source the string to parse
    336         * @param pos input/ouput parsing parameter.
    337         * @return the parsed object.
    338         * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
    339         */
    340        @Override
    341        public Object parseObject(String source, ParsePosition pos) {
    342            return parse(source, pos);
    343        }
    344
    345        /**
    346         * Modify the imaginaryCharacter.
    347         * @param imaginaryCharacter The new imaginaryCharacter value.
    348         * @throws IllegalArgumentException if <code>imaginaryCharacter</code> is
    349         *         <code>null</code> or an empty string.
    350         */
    351        public void setImaginaryCharacter(String imaginaryCharacter) {
    352            if (imaginaryCharacter == null || imaginaryCharacter.length() == 0) {
    353                throw MathRuntimeException.createIllegalArgumentException(
    354                      LocalizedFormats.EMPTY_STRING_FOR_IMAGINARY_CHARACTER);
    355            }
    356            this.imaginaryCharacter = imaginaryCharacter;
    357        }
    358
    359        /**
    360         * Modify the imaginaryFormat.
    361         * @param imaginaryFormat The new imaginaryFormat value.
    362         * @throws NullArgumentException if {@code imaginaryFormat} is {@code null}.
    363         */
    364        public void setImaginaryFormat(NumberFormat imaginaryFormat) {
    365            if (imaginaryFormat == null) {
    366                throw new NullArgumentException(LocalizedFormats.IMAGINARY_FORMAT);
    367            }
    368            this.imaginaryFormat = imaginaryFormat;
    369        }
    370
    371        /**
    372         * Modify the realFormat.
    373         * @param realFormat The new realFormat value.
    374         * @throws NullArgumentException if {@code realFormat} is {@code null}.
    375         */
    376        public void setRealFormat(NumberFormat realFormat) {
    377            if (realFormat == null) {
    378                throw new NullArgumentException(LocalizedFormats.REAL_FORMAT);
    379            }
    380            this.realFormat = realFormat;
    381        }
    382
    383    }