View Javadoc

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  package org.apache.commons.math.util;
18  
19  import java.text.FieldPosition;
20  import java.text.Format;
21  import java.text.NumberFormat;
22  import java.text.ParsePosition;
23  import java.util.Locale;
24  
25  /**
26   * Base class for formatters of composite objects (complex numbers, vectors ...).
27   *
28   * @author Apache Software Foundation
29   * @version $Revision: 705231 $ $Date: 2008-10-16 08:49:13 -0400 (Thu, 16 Oct 2008) $
30   */
31  public abstract class CompositeFormat extends Format {
32  
33      /** Serializable version identifier. */
34      private static final long serialVersionUID = 5358685519349262494L;
35  
36      /**
37       * Create a default number format.  The default number format is based on
38       * {@link NumberFormat#getInstance()} with the only customizing that the
39       * maximum number of fraction digits is set to 2.  
40       * @return the default number format.
41       */
42      protected static NumberFormat getDefaultNumberFormat() {
43          return getDefaultNumberFormat(Locale.getDefault());
44      }
45  
46      /**
47       * Create a default number format.  The default number format is based on
48       * {@link NumberFormat#getInstance(java.util.Locale)} with the only
49       * customizing that the maximum number of fraction digits is set to 2.  
50       * @param locale the specific locale used by the format.
51       * @return the default number format specific to the given locale.
52       */
53      protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
54          final NumberFormat nf = NumberFormat.getInstance(locale);
55          nf.setMaximumFractionDigits(2);
56          return nf;
57      }
58  
59      /**
60       * Parses <code>source</code> until a non-whitespace character is found.
61       *
62       * @param source the string to parse
63       * @param pos input/ouput parsing parameter.  On output, <code>pos</code>
64       *        holds the index of the next non-whitespace character.
65       */
66      protected void parseAndIgnoreWhitespace(final String source,
67                                              final ParsePosition pos) {
68          parseNextCharacter(source, pos);
69          pos.setIndex(pos.getIndex() - 1);
70      }
71  
72      /**
73       * Parses <code>source</code> until a non-whitespace character is found.
74       *
75       * @param source the string to parse
76       * @param pos input/ouput parsing parameter.
77       * @return the first non-whitespace character.
78       */
79      protected char parseNextCharacter(final String source,
80                                        final ParsePosition pos) {
81           int index = pos.getIndex();
82           final int n = source.length();
83           char ret = 0;
84      
85           if (index < n) {
86               char c;
87               do {
88                   c = source.charAt(index++);
89               } while (Character.isWhitespace(c) && index < n);
90               pos.setIndex(index);
91           
92               if (index < n) {
93                   ret = c;
94               }
95           }
96           
97           return ret;
98      }
99  
100     /**
101      * Parses <code>source</code> for special double values.  These values
102      * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
103      *
104      * @param source the string to parse
105      * @param value the special value to parse.
106      * @param pos input/ouput parsing parameter.
107      * @return the special number.
108      */
109     private Number parseNumber(final String source, final double value,
110                                final ParsePosition pos) {
111         Number ret = null;
112         
113         StringBuffer sb = new StringBuffer();
114         sb.append('(');
115         sb.append(value);
116         sb.append(')');
117         
118         final int n = sb.length();
119         final int startIndex = pos.getIndex();
120         final int endIndex = startIndex + n;
121         if (endIndex < source.length()) {
122             if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
123                 ret = Double.valueOf(value);
124                 pos.setIndex(endIndex);
125             }
126         }
127         
128         return ret;
129     }
130 
131     /**
132      * Parses <code>source</code> for a number.  This method can parse normal,
133      * numeric values as well as special values.  These special values include
134      * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
135      *
136      * @param source the string to parse
137      * @param format the number format used to parse normal, numeric values.
138      * @param pos input/ouput parsing parameter.
139      * @return the parsed number.
140      */
141     protected Number parseNumber(final String source, final NumberFormat format,
142                                  final ParsePosition pos) {
143         final int startIndex = pos.getIndex();
144         Number number = format.parse(source, pos);
145         final int endIndex = pos.getIndex();
146         
147         // check for error parsing number
148         if (startIndex == endIndex) {
149             // try parsing special numbers
150             final double[] special = {
151                 Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
152             };
153             for (int i = 0; i < special.length; ++i) {
154                 number = parseNumber(source, special[i], pos);
155                 if (number != null) {
156                     break;
157                 }
158             }
159         }
160         
161         return number;
162     }
163 
164     /**
165      * Parse <code>source</code> for an expected fixed string.
166      * @param source the string to parse
167      * @param expected expected string
168      * @param pos input/ouput parsing parameter.
169      * @return true if the expected string was there
170      */
171     protected boolean parseFixedstring(final String source, final String expected,
172                                        final ParsePosition pos) {
173 
174         final int startIndex = pos.getIndex();
175         final int endIndex = startIndex + expected.length();
176         if ((startIndex >= source.length()) ||
177             (endIndex > source.length()) ||
178             (source.substring(startIndex, endIndex).compareTo(expected) != 0)) {
179             // set index back to start, error index should be the start index
180             pos.setIndex(startIndex);
181             pos.setErrorIndex(startIndex);
182             return false;
183         }
184 
185         // the string was here
186         pos.setIndex(endIndex);
187         return true;
188 
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     protected StringBuffer formatDouble(final double value, final NumberFormat format,
209                                         final StringBuffer toAppendTo,
210                                         final FieldPosition pos) {
211         if( Double.isNaN(value) || Double.isInfinite(value) ) {
212             toAppendTo.append('(');
213             toAppendTo.append(value);
214             toAppendTo.append(')');
215         } else {
216             format.format(value, toAppendTo, pos);
217         }
218         return toAppendTo;
219     }
220 
221 }