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.math3.util;
18  
19  import java.text.FieldPosition;
20  import java.text.NumberFormat;
21  import java.text.ParsePosition;
22  import java.util.Locale;
23  
24  /**
25   * Base class for formatters of composite objects (complex numbers, vectors ...).
26   *
27   * @version $Id: CompositeFormat.java 1462503 2013-03-29 15:48:27Z luc $
28   */
29  public class CompositeFormat {
30  
31      /**
32       * Class contains only static methods.
33       */
34      private CompositeFormat() {}
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 10.
40       * @return the default number format.
41       */
42      public 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 10.
50       * @param locale the specific locale used by the format.
51       * @return the default number format specific to the given locale.
52       */
53      public static NumberFormat getDefaultNumberFormat(final Locale locale) {
54          final NumberFormat nf = NumberFormat.getInstance(locale);
55          nf.setMaximumFractionDigits(10);
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/output parsing parameter.  On output, <code>pos</code>
64       *        holds the index of the next non-whitespace character.
65       */
66      public static 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/output parsing parameter.
77       * @return the first non-whitespace character.
78       */
79      public static 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/output parsing parameter.
107      * @return the special number.
108      */
109     private static Number parseNumber(final String source, final double value,
110                                       final ParsePosition pos) {
111         Number ret = null;
112 
113         StringBuilder sb = new StringBuilder();
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             source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
123             ret = Double.valueOf(value);
124             pos.setIndex(endIndex);
125         }
126 
127         return ret;
128     }
129 
130     /**
131      * Parses <code>source</code> for a number.  This method can parse normal,
132      * numeric values as well as special values.  These special values include
133      * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
134      *
135      * @param source the string to parse
136      * @param format the number format used to parse normal, numeric values.
137      * @param pos input/output parsing parameter.
138      * @return the parsed number.
139      */
140     public static Number parseNumber(final String source, final NumberFormat format,
141                                      final ParsePosition pos) {
142         final int startIndex = pos.getIndex();
143         Number number = format.parse(source, pos);
144         final int endIndex = pos.getIndex();
145 
146         // check for error parsing number
147         if (startIndex == endIndex) {
148             // try parsing special numbers
149             final double[] special = {
150                 Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
151             };
152             for (int i = 0; i < special.length; ++i) {
153                 number = parseNumber(source, special[i], pos);
154                 if (number != null) {
155                     break;
156                 }
157             }
158         }
159 
160         return number;
161     }
162 
163     /**
164      * Parse <code>source</code> for an expected fixed string.
165      * @param source the string to parse
166      * @param expected expected string
167      * @param pos input/output parsing parameter.
168      * @return true if the expected string was there
169      */
170     public static boolean parseFixedstring(final String source,
171                                            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      * Formats a double value to produce a string.  In general, the value is
192      * formatted using the formatting rules of <code>format</code>.  There are
193      * three exceptions to this:
194      * <ol>
195      * <li>NaN is formatted as '(NaN)'</li>
196      * <li>Positive infinity is formatted as '(Infinity)'</li>
197      * <li>Negative infinity is formatted as '(-Infinity)'</li>
198      * </ol>
199      *
200      * @param value the double to format.
201      * @param format the format used.
202      * @param toAppendTo where the text is to be appended
203      * @param pos On input: an alignment field, if desired. On output: the
204      *            offsets of the alignment field
205      * @return the value passed in as toAppendTo.
206      */
207     public static StringBuffer formatDouble(final double value, final NumberFormat format,
208                                             final StringBuffer toAppendTo,
209                                             final FieldPosition pos) {
210         if( Double.isNaN(value) || Double.isInfinite(value) ) {
211             toAppendTo.append('(');
212             toAppendTo.append(value);
213             toAppendTo.append(')');
214         } else {
215             format.format(value, toAppendTo, pos);
216         }
217         return toAppendTo;
218     }
219 }