KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > math > complex > ComplexFormat


1 /*
2  * Copyright 2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.commons.math.complex;
18
19 import java.io.Serializable JavaDoc;
20 import java.text.FieldPosition JavaDoc;
21 import java.text.Format JavaDoc;
22 import java.text.NumberFormat JavaDoc;
23 import java.text.ParseException JavaDoc;
24 import java.text.ParsePosition JavaDoc;
25 import java.util.Locale JavaDoc;
26
27 /**
28  * Formats a Complex number in cartesian format "Re(c) + Im(c)i". 'i' can
29  * be replaced with 'j', and the number format for both real and imaginary parts
30  * can be configured.
31  *
32  * @author Apache Software Foundation
33  * @version $Revision$ $Date: 2005-06-26 15:20:57 -0700 (Sun, 26 Jun 2005) $
34  */

35 public class ComplexFormat extends Format JavaDoc implements Serializable JavaDoc {
36     
37     /** Serializable version identifier */
38     static final long serialVersionUID = -6337346779577272306L;
39     
40     /** The default imaginary character. */
41     private static final String JavaDoc DEFAULT_IMAGINARY_CHARACTER = "i";
42     
43     /** The notation used to signify the imaginary part of the complex number. */
44     private String JavaDoc imaginaryCharacter;
45     
46     /** The format used for the imaginary part. */
47     private NumberFormat JavaDoc imaginaryFormat;
48
49     /** The format used for the real part. */
50     private NumberFormat JavaDoc realFormat;
51     
52     /**
53      * Create an instance with the default imaginary character, 'i', and the
54      * default number format for both real and imaginary parts.
55      */

56     public ComplexFormat() {
57         this(DEFAULT_IMAGINARY_CHARACTER, getDefaultNumberFormat());
58     }
59
60     /**
61      * Create an instance with a custom number format for both real and
62      * imaginary parts.
63      * @param format the custom format for both real and imaginary parts.
64      */

65     public ComplexFormat(NumberFormat JavaDoc format) {
66         this(DEFAULT_IMAGINARY_CHARACTER, format);
67     }
68     
69     /**
70      * Create an instance with a custom number format for the real part and a
71      * custom number format for the imaginary part.
72      * @param realFormat the custom format for the real part.
73      * @param imaginaryFormat the custom format for the imaginary part.
74      */

75     public ComplexFormat(NumberFormat JavaDoc realFormat,
76             NumberFormat JavaDoc imaginaryFormat) {
77         this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat);
78     }
79     
80     /**
81      * Create an instance with a custom imaginary character, and the default
82      * number format for both real and imaginary parts.
83      * @param imaginaryCharacter The custom imaginary character.
84      */

85     public ComplexFormat(String JavaDoc imaginaryCharacter) {
86         this(imaginaryCharacter, getDefaultNumberFormat());
87     }
88     
89     /**
90      * Create an instance with a custom imaginary character, and a custom number
91      * format for both real and imaginary parts.
92      * @param imaginaryCharacter The custom imaginary character.
93      * @param format the custom format for both real and imaginary parts.
94      */

95     public ComplexFormat(String JavaDoc imaginaryCharacter, NumberFormat JavaDoc format) {
96         this(imaginaryCharacter, format, (NumberFormat JavaDoc)format.clone());
97     }
98     
99     /**
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 JavaDoc imaginaryCharacter, NumberFormat JavaDoc realFormat,
108             NumberFormat JavaDoc imaginaryFormat) {
109         super();
110         setImaginaryCharacter(imaginaryCharacter);
111         setImaginaryFormat(imaginaryFormat);
112         setRealFormat(realFormat);
113     }
114
115     /**
116      * This static method calls formatComplex() on a default instance of
117      * ComplexFormat.
118      *
119      * @param c Complex object to format
120      * @return A formatted number in the form "Re(c) + Im(c)i"
121      */

122     public static String JavaDoc formatComplex( Complex c ) {
123         return getInstance().format( c );
124     }
125     
126     /**
127      * Formats a {@link Complex} object to produce a string.
128      *
129      * @param complex the object to format.
130      * @param toAppendTo where the text is to be appended
131      * @param pos On input: an alignment field, if desired. On output: the
132      * offsets of the alignment field
133      * @return the value passed in as toAppendTo.
134      */

135     public StringBuffer JavaDoc format(Complex complex, StringBuffer JavaDoc toAppendTo,
136             FieldPosition JavaDoc pos) {
137         
138         pos.setBeginIndex(0);
139         pos.setEndIndex(0);
140
141         // format real
142
double re = complex.getReal();
143         formatDouble(re, getRealFormat(), toAppendTo, pos);
144         
145         // format sign and imaginary
146
double im = complex.getImaginary();
147         if (im < 0.0) {
148             toAppendTo.append(" - ");
149             formatDouble(-im, getImaginaryFormat(), toAppendTo, pos);
150             toAppendTo.append(getImaginaryCharacter());
151         } else if (im > 0.0 || Double.isNaN(im)) {
152             toAppendTo.append(" + ");
153             formatDouble(im, getImaginaryFormat(), toAppendTo, pos);
154             toAppendTo.append(getImaginaryCharacter());
155         }
156         
157         return toAppendTo;
158     }
159     
160     /**
161      * Formats a object to produce a string. <code>obj</code> must be either a
162      * {@link Complex} object or a {@link Number} object. Any other type of
163      * object will result in an {@link IllegalArgumentException} being thrown.
164      *
165      * @param obj the object to format.
166      * @param toAppendTo where the text is to be appended
167      * @param pos On input: an alignment field, if desired. On output: the
168      * offsets of the alignment field
169      * @return the value passed in as toAppendTo.
170      * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
171      * @throws IllegalArgumentException is <code>obj</code> is not a valid type.
172      */

173     public StringBuffer JavaDoc format(Object JavaDoc obj, StringBuffer JavaDoc toAppendTo,
174             FieldPosition JavaDoc pos) {
175         
176         StringBuffer JavaDoc ret = null;
177         
178         if (obj instanceof Complex) {
179             ret = format( (Complex)obj, toAppendTo, pos);
180         } else if (obj instanceof Number JavaDoc) {
181             ret = format( new Complex(((Number JavaDoc)obj).doubleValue(), 0.0),
182                 toAppendTo, pos);
183         } else {
184             throw new IllegalArgumentException JavaDoc(
185                 "Cannot format given Object as a Date");
186         }
187         
188         return ret;
189     }
190
191     /**
192      * Formats a double value to produce a string. In general, the value is
193      * formatted using the formatting rules of <code>format</code>. There are
194      * three exceptions to this:
195      * <ol>
196      * <li>NaN is formatted as '(NaN)'</li>
197      * <li>Positive infinity is formatted as '(Infinity)'</li>
198      * <li>Negative infinity is formatted as '(-Infinity)'</li>
199      * </ol>
200      *
201      * @param value the double to format.
202      * @param format the format used.
203      * @param toAppendTo where the text is to be appended
204      * @param pos On input: an alignment field, if desired. On output: the
205      * offsets of the alignment field
206      * @return the value passed in as toAppendTo.
207      */

208     private StringBuffer JavaDoc formatDouble(double value, NumberFormat JavaDoc format,
209             StringBuffer JavaDoc toAppendTo, FieldPosition JavaDoc pos) {
210         if( Double.isNaN(value) || Double.isInfinite(value) ) {
211             toAppendTo.append('(');
212             toAppendTo.append(value);
213             toAppendTo.append(')');
214         } else {
215             getRealFormat().format(value, toAppendTo, pos);
216         }
217         return toAppendTo;
218     }
219     
220     /**
221      * Get the set of locales for which complex formats are available. This
222      * is the same set as the {@link NumberFormat} set.
223      * @return available complex format locales.
224      */

225     public static Locale JavaDoc[] getAvailableLocales() {
226         return NumberFormat.getAvailableLocales();
227     }
228     
229     /**
230      * Create a default number format. The default number format is based on
231      * {@link NumberFormat#getInstance()} with the only customizing is the
232      * maximum number of fraction digits, which is set to 2.
233      * @return the default number format.
234      */

235     private static NumberFormat JavaDoc getDefaultNumberFormat() {
236         return getDefaultNumberFormat(Locale.getDefault());
237     }
238     
239     /**
240      * Create a default number format. The default number format is based on
241      * {@link NumberFormat#getInstance(java.util.Locale)} with the only
242      * customizing is the maximum number of fraction digits, which is set to 2.
243      * @param locale the specific locale used by the format.
244      * @return the default number format specific to the given locale.
245      */

246     private static NumberFormat JavaDoc getDefaultNumberFormat(Locale JavaDoc locale) {
247         NumberFormat JavaDoc nf = NumberFormat.getInstance(locale);
248         nf.setMaximumFractionDigits(2);
249         return nf;
250     }
251     
252     /**
253      * Access the imaginaryCharacter.
254      * @return the imaginaryCharacter.
255      */

256     public String JavaDoc getImaginaryCharacter() {
257         return imaginaryCharacter;
258     }
259     
260     /**
261      * Access the imaginaryFormat.
262      * @return the imaginaryFormat.
263      */

264     public NumberFormat JavaDoc getImaginaryFormat() {
265         return imaginaryFormat;
266     }
267     
268     /**
269      * Returns the default complex format for the current locale.
270      * @return the default complex format.
271      */

272     public static ComplexFormat getInstance() {
273         return getInstance(Locale.getDefault());
274     }
275     
276     /**
277      * Returns the default complex format for the given locale.
278      * @param locale the specific locale used by the format.
279      * @return the complex format specific to the given locale.
280      */

281     public static ComplexFormat getInstance(Locale JavaDoc locale) {
282         NumberFormat JavaDoc f = getDefaultNumberFormat(locale);
283         return new ComplexFormat(f);
284     }
285     
286     /**
287      * Access the realFormat.
288      * @return the realFormat.
289      */

290     public NumberFormat JavaDoc getRealFormat() {
291         return realFormat;
292     }
293
294     /**
295      * Parses a string to produce a {@link Complex} object.
296      *
297      * @param source the string to parse
298      * @return the parsed {@link Complex} object.
299      * @exception ParseException if the beginning of the specified string
300      * cannot be parsed.
301      */

302     public Complex parse(String JavaDoc source) throws ParseException JavaDoc {
303         ParsePosition JavaDoc parsePosition = new ParsePosition JavaDoc(0);
304         Complex result = parse(source, parsePosition);
305         if (parsePosition.getIndex() == 0) {
306             throw new ParseException JavaDoc("Unparseable complex number: \"" + source +
307                 "\"", parsePosition.getErrorIndex());
308         }
309         return result;
310     }
311     
312     /**
313      * Parses a string to produce a {@link Complex} object.
314      *
315      * @param source the string to parse
316      * @param pos input/ouput parsing parameter.
317      * @return the parsed {@link Complex} object.
318      */

319     public Complex parse(String JavaDoc source, ParsePosition JavaDoc pos) {
320         int initialIndex = pos.getIndex();
321
322         // parse whitespace
323
parseAndIgnoreWhitespace(source, pos);
324
325         // parse real
326
Number JavaDoc re = parseNumber(source, getRealFormat(), pos);
327         if (re == null) {
328             // invalid real number
329
// set index back to initial, error index should already be set
330
// character examined.
331
pos.setIndex(initialIndex);
332             return null;
333         }
334
335         // parse sign
336
int startIndex = pos.getIndex();
337         char c = parseNextCharacter(source, pos);
338         int sign = 0;
339         switch (c) {
340         case 0 :
341             // no sign
342
// return real only complex number
343
return new Complex(re.doubleValue(), 0.0);
344         case '-' :
345             sign = -1;
346             break;
347         case '+' :
348             sign = 1;
349             break;
350         default :
351             // invalid sign
352
// set index back to initial, error index should be the last
353
// character examined.
354
pos.setIndex(initialIndex);
355             pos.setErrorIndex(startIndex);
356             return null;
357         }
358
359         // parse whitespace
360
parseAndIgnoreWhitespace(source, pos);
361
362         // parse imaginary
363
Number JavaDoc im = parseNumber(source, getRealFormat(), pos);
364         if (im == null) {
365             // invalid imaginary number
366
// set index back to initial, error index should already be set
367
// character examined.
368
pos.setIndex(initialIndex);
369             return null;
370         }
371
372         // parse imaginary character
373
int n = getImaginaryCharacter().length();
374         startIndex = pos.getIndex();
375         int endIndex = startIndex + n;
376         if (source.substring(startIndex, endIndex).compareTo(
377             getImaginaryCharacter()) != 0) {
378             // set index back to initial, error index should be the start index
379
// character examined.
380
pos.setIndex(initialIndex);
381             pos.setErrorIndex(startIndex);
382             return null;
383         }
384         pos.setIndex(endIndex);
385
386         return new Complex(re.doubleValue(), im.doubleValue() * sign);
387     }
388      
389     /**
390      * Parses <code>source</code> until a non-whitespace character is found.
391      *
392      * @param source the string to parse
393      * @param pos input/ouput parsing parameter. On output, <code>pos</code>
394      * holds the index of the next non-whitespace character.
395      */

396     private void parseAndIgnoreWhitespace(String JavaDoc source, ParsePosition JavaDoc pos) {
397         parseNextCharacter(source, pos);
398         pos.setIndex(pos.getIndex() - 1);
399     }
400
401     /**
402      * Parses <code>source</code> until a non-whitespace character is found.
403      *
404      * @param source the string to parse
405      * @param pos input/ouput parsing parameter.
406      * @return the first non-whitespace character.
407      */

408     private char parseNextCharacter(String JavaDoc source, ParsePosition JavaDoc pos) {
409          int index = pos.getIndex();
410          int n = source.length();
411          char ret = 0;
412
413          if (index < n) {
414              char c;
415              do {
416                  c = source.charAt(index++);
417              } while (Character.isWhitespace(c) && index < n);
418              pos.setIndex(index);
419          
420              if (index < n) {
421                  ret = c;
422              }
423          }
424          
425          return ret;
426     }
427     
428     /**
429      * Parses <code>source</code> for a special double values. These values
430      * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
431      *
432      * @param source the string to parse
433      * @param value the special value to parse.
434      * @param pos input/ouput parsing parameter.
435      * @return the special number.
436      */

437     private Number JavaDoc parseNumber(String JavaDoc source, double value, ParsePosition JavaDoc pos) {
438         Number JavaDoc ret = null;
439         
440         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
441         sb.append('(');
442         sb.append(value);
443         sb.append(')');
444         
445         int n = sb.length();
446         int startIndex = pos.getIndex();
447         int endIndex = startIndex + n;
448         if (endIndex < source.length()) {
449             if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
450                 ret = new Double JavaDoc(value);
451                 pos.setIndex(endIndex);
452             }
453         }
454         
455         return ret;
456     }
457     
458     /**
459      * Parses <code>source</code> for a number. This method can parse normal,
460      * numeric values as well as special values. These special values include
461      * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
462      *
463      * @param source the string to parse
464      * @param format the number format used to parse normal, numeric values.
465      * @param pos input/ouput parsing parameter.
466      * @return the parsed number.
467      */

468     private Number JavaDoc parseNumber(String JavaDoc source, NumberFormat JavaDoc format, ParsePosition JavaDoc pos) {
469         int startIndex = pos.getIndex();
470         Number JavaDoc number = getRealFormat().parse(source, pos);
471         int endIndex = pos.getIndex();
472         
473         // check for error parsing number
474
if (startIndex == endIndex) {
475             // try parsing special numbers
476
double[] special = {Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
477             for (int i = 0; i < special.length; ++i) {
478                 number = parseNumber(source, special[i], pos);
479                 if (number != null) {
480                     break;
481                 }
482             }
483         }
484         
485         return number;
486     }
487
488     /**
489      * Parses a string to produce a object.
490      *
491      * @param source the string to parse
492      * @param pos input/ouput parsing parameter.
493      * @return the parsed object.
494      * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
495      */

496     public Object JavaDoc parseObject(String JavaDoc source, ParsePosition JavaDoc pos) {
497         return parse(source, pos);
498     }
499     /**
500      * Modify the imaginaryCharacter.
501      * @param imaginaryCharacter The new imaginaryCharacter value.
502      * @throws IllegalArgumentException if <code>imaginaryCharacter</code> is
503      * <code>null</code> or an empty string.
504      */

505     public void setImaginaryCharacter(String JavaDoc imaginaryCharacter) {
506         if (imaginaryCharacter == null || imaginaryCharacter.length() == 0) {
507             throw new IllegalArgumentException JavaDoc(
508                 "imaginaryCharacter must be a non-empty string.");
509         }
510         this.imaginaryCharacter = imaginaryCharacter;
511     }
512     
513     /**
514      * Modify the imaginaryFormat.
515      * @param imaginaryFormat The new imaginaryFormat value.
516      * @throws IllegalArgumentException if <code>imaginaryFormat</code> is
517      * <code>null</code>.
518      */

519     public void setImaginaryFormat(NumberFormat JavaDoc imaginaryFormat) {
520         if (imaginaryFormat == null) {
521             throw new IllegalArgumentException JavaDoc(
522                 "imaginaryFormat can not be null.");
523         }
524         this.imaginaryFormat = imaginaryFormat;
525     }
526     
527     /**
528      * Modify the realFormat.
529      * @param realFormat The new realFormat value.
530      * @throws IllegalArgumentException if <code>realFormat</code> is
531      * <code>null</code>.
532      */

533     public void setRealFormat(NumberFormat JavaDoc realFormat) {
534         if (realFormat == null) {
535             throw new IllegalArgumentException JavaDoc(
536                 "realFormat can not be null.");
537         }
538         this.realFormat = realFormat;
539     }
540 }
541
Popular Tags