Home | History | Annotate | Download | only in complex
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package org.apache.commons.math.complex;
     19 
     20 import java.text.FieldPosition;
     21 import java.text.NumberFormat;
     22 import java.text.ParseException;
     23 import java.text.ParsePosition;
     24 import java.util.Locale;
     25 
     26 import org.apache.commons.math.MathRuntimeException;
     27 import org.apache.commons.math.exception.util.LocalizedFormats;
     28 import org.apache.commons.math.util.CompositeFormat;
     29 import org.apache.commons.math.exception.NullArgumentException;
     30 
     31 /**
     32  * Formats a Complex number in cartesian format "Re(c) + Im(c)i".  'i' can
     33  * be replaced with 'j' (or anything else), and the number format for both real
     34  * and imaginary parts can be configured.
     35  *
     36  * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 aot 2010) $
     37  */
     38 public class ComplexFormat extends CompositeFormat {
     39 
     40     /** Serializable version identifier */
     41     private static final long serialVersionUID = -3343698360149467646L;
     42 
     43      /** The default imaginary character. */
     44     private static final String DEFAULT_IMAGINARY_CHARACTER = "i";
     45 
     46     /** The notation used to signify the imaginary part of the complex number. */
     47     private String imaginaryCharacter;
     48 
     49     /** The format used for the imaginary part. */
     50     private NumberFormat imaginaryFormat;
     51 
     52     /** The format used for the real part. */
     53     private NumberFormat realFormat;
     54 
     55     /**
     56      * Create an instance with the default imaginary character, 'i', and the
     57      * default number format for both real and imaginary parts.
     58      */
     59     public ComplexFormat() {
     60         this(DEFAULT_IMAGINARY_CHARACTER, getDefaultNumberFormat());
     61     }
     62 
     63     /**
     64      * Create an instance with a custom number format for both real and
     65      * imaginary parts.
     66      * @param format the custom format for both real and imaginary parts.
     67      */
     68     public ComplexFormat(NumberFormat format) {
     69         this(DEFAULT_IMAGINARY_CHARACTER, format);
     70     }
     71 
     72     /**
     73      * Create an instance with a custom number format for the real part and a
     74      * custom number format for the imaginary part.
     75      * @param realFormat the custom format for the real part.
     76      * @param imaginaryFormat the custom format for the imaginary part.
     77      */
     78     public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat) {
     79         this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat);
     80     }
     81 
     82     /**
     83      * Create an instance with a custom imaginary character, and the default
     84      * number format for both real and imaginary parts.
     85      * @param imaginaryCharacter The custom imaginary character.
     86      */
     87     public ComplexFormat(String imaginaryCharacter) {
     88         this(imaginaryCharacter, getDefaultNumberFormat());
     89     }
     90 
     91     /**
     92      * Create an instance with a custom imaginary character, and a custom number
     93      * format for both real and imaginary parts.
     94      * @param imaginaryCharacter The custom imaginary character.
     95      * @param format the custom format for both real and imaginary parts.
     96      */
     97     public ComplexFormat(String imaginaryCharacter, NumberFormat format) {
     98         this(imaginaryCharacter, format, (NumberFormat)format.clone());
     99     }
    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 }
    384