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