View Javadoc

1   /*
2    $Id: Numbers.java,v 1.3 2004/04/07 20:19:20 cpoirier Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  
47  package org.codehaus.groovy.syntax;
48  
49  import java.math.BigInteger;
50  import java.math.BigDecimal;
51  
52  /***
53   *  Helper class for processing Groovy numeric literals.
54   *
55   *  @author Brian Larson
56   *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
57   *
58   *  @version $Id: Numbers.java,v 1.3 2004/04/07 20:19:20 cpoirier Exp $
59   */
60  
61  public class Numbers
62  {
63  
64  
65  
66    //---------------------------------------------------------------------------
67    // LEXING SUPPORT
68  
69  
70     /***
71      *  Returns true if the specified character is a base-10 digit.
72      */
73  
74      public static boolean isDigit( char c )
75      {
76          return c >= '0' && c <= '9';
77      }
78  
79  
80     /***
81      *  Returns true if the specific character is a base-8 digit.
82      */
83  
84      public static boolean isOctalDigit( char c )
85      {
86          return c >= '0' && c <= '7';
87      }
88  
89  
90     /***
91      *  Returns true if the specified character is a base-16 digit.
92      */
93  
94      public static boolean isHexDigit( char c )
95      {
96          return isDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
97      }
98  
99  
100 
101    /***
102     *  Returns true if the specified character is a valid type specifier
103     *  for a numeric value.
104     */
105 
106     public static boolean isNumericTypeSpecifier( char c, boolean isDecimal )
107     {
108         if( isDecimal )
109         {
110             switch( c )
111             {
112                 case 'G':
113                 case 'g':
114                 case 'D':
115                 case 'd':
116                 case 'F':
117                 case 'f':
118                     return true;
119             }
120         }
121         else
122         {
123             switch( c )
124             {
125                 case 'G':
126                 case 'g':
127                 case 'I':
128                 case 'i':
129                 case 'L':
130                 case 'l':
131                     return true;
132             }
133         }
134 
135         return false;
136     }
137 
138 
139 
140 
141 
142   //---------------------------------------------------------------------------
143   // PARSING SUPPORT
144 
145 
146     private static final BigInteger MAX_LONG    = BigInteger.valueOf(Long.MAX_VALUE);
147     private static final BigInteger MIN_LONG    = BigInteger.valueOf(Long.MIN_VALUE);
148 
149     private static final BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
150     private static final BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
151 
152     private static final BigDecimal MAX_DOUBLE  = new BigDecimal(String.valueOf(Double.MAX_VALUE));
153     private static final BigDecimal MIN_DOUBLE  = MAX_DOUBLE.negate();
154 
155     private static final BigDecimal MAX_FLOAT   = new BigDecimal(String.valueOf(Float.MAX_VALUE));
156     private static final BigDecimal MIN_FLOAT   = MAX_FLOAT.negate();
157 
158 
159 
160    /***
161     *  Builds a Number from the given integer descriptor.  Creates the narrowest
162     *  type possible, or a specific type, if specified.
163     *
164     *  @param  text literal text to parse
165     *  @return instantiated Number object
166     *  @throws NumberFormatException if the number does not fit within the type
167     *          requested by the type specifier suffix (invalid numbers don't make
168     *          it here)
169     */
170 
171     public static Number parseInteger( String text )
172     {
173         char c = ' ';
174         int length = text.length();
175 
176 
177         //
178         // Strip off the sign, if present
179 
180         boolean negative = false;
181         if( (c = text.charAt(0)) == '-' || c == '+' )
182         {
183             negative = (c == '-');
184             text = text.substring( 1, length );
185             length -= 1;
186         }
187 
188 
189         //
190         // Determine radix (default is 10).
191 
192         int radix = 10;
193         if( text.charAt(0) == '0' && length > 1 )
194         {
195             if( (c = text.charAt(1)) == 'X' || c == 'x' )
196             {
197                 radix = 16;
198                 text = text.substring( 2, length);
199                 length -= 2;
200             }
201             else
202             {
203                 radix = 8;
204             }
205         }
206 
207 
208         //
209         // Strip off any type specifier and convert it to lower
210         // case, if present.
211 
212         char type = 'x';  // pick best fit
213         if( isNumericTypeSpecifier(text.charAt(length-1), false) )
214         {
215             type = Character.toLowerCase( text.charAt(length-1) );
216             text = text.substring( 0, length-1);
217 
218             length -= 1;
219         }
220 
221 
222         //
223         // Add the sign back, if necessary
224 
225         if( negative )
226         {
227             text = "-" + text;
228         }
229 
230 
231         //
232         // Build the specified type or, if no type was specified, the
233         // smallest type in which the number will fit.
234 
235         switch (type)
236         {
237             case 'i':
238                 return new Integer( Integer.parseInt(text, radix) );
239 
240             case 'l':
241                 return new Long( Long.parseLong(text, radix) );
242 
243             case 'g':
244                 return new BigInteger( text, radix );
245 
246             default:
247 
248                 //
249                 // If not specified, we will return the narrowest possible
250                 // of Integer, Long, and BigInteger.
251 
252                 BigInteger value = new BigInteger( text, radix );
253 
254                 if( value.compareTo(MAX_INTEGER) <= 0 && value.compareTo(MIN_INTEGER) >= 0 )
255                 {
256                     return new Integer(value.intValue());
257                 }
258                 else if( value.compareTo(MAX_LONG) <= 0 && value.compareTo(MIN_LONG) >= 0 )
259                 {
260                     return new Long(value.longValue());
261                 }
262 
263                 return value;
264         }
265     }
266 
267 
268 
269    /***
270     *  Builds a Number from the given decimal descriptor.  Uses BigDecimal,
271     *  unless, Double or Float is requested.
272     *
273     *  @param  text literal text to parse
274     *  @return instantiated Number object
275     *  @throws NumberFormatException if the number does not fit within the type
276     *          requested by the type specifier suffix (invalid numbers don't make
277     *          it here)
278     */
279 
280     public static Number parseDecimal( String text )
281     {
282         int length = text.length();
283 
284 
285         //
286         // Strip off any type specifier and convert it to lower
287         // case, if present.
288 
289         char type = 'x';
290         if( isNumericTypeSpecifier(text.charAt(length-1), true) )
291         {
292             type = Character.toLowerCase( text.charAt(length-1) );
293             text = text.substring( 0, length-1 );
294 
295             length -= 1;
296         }
297 
298 
299         //
300         // Build the specified type or default to BigDecimal
301 
302         BigDecimal value = new BigDecimal( text );
303         switch( type )
304         {
305             case 'f':
306                 if( value.compareTo(MAX_FLOAT) <= 0 && value.compareTo(MIN_FLOAT) >= 0)
307                 {
308                     return new Float( text );
309                 }
310                 throw new NumberFormatException( "out of range" );
311 
312             case 'd':
313                 if( value.compareTo(MAX_DOUBLE) <= 0 && value.compareTo(MIN_DOUBLE) >= 0)
314                 {
315                     return new Double( text );
316                 }
317                 throw new NumberFormatException( "out of range" );
318 
319             case 'g':
320             default:
321                 return value;
322         }
323     }
324 
325 }