View Javadoc
1   /*
2    * J.A.D.E. Java(TM) Addition to Default Environment.
3    * Latest release available at http://jade.dautelle.com/
4    * This class is public domain (not copyrighted).
5    */
6   package org.codehaus.plexus.util;
7   
8   /**
9    * <p>
10   * This class provides utility methods to parse <code>CharSequence</code> into primitive types and to format primitive
11   * types into <code>StringBuffer</code>.
12   * </p>
13   * <p>
14   * Methods from this utility class <b>do not create temporary objects</b> and are typically faster than standard library
15   * methods (e.g {@link #parseDouble} is up to 15x faster than <code>Double.parseDouble</code>).
16   * </p>
17   * For class instances, formatting is typically performed using specialized <code>java.text.Format</code>
18   * (<code>Locale</code> sensitive) and/or using conventional methods (class sensitive). For example:
19   * 
20   * <pre>
21   *     public class Foo {
22   *         public static Foo valueOf(CharSequence chars) {...} // Parses.
23   *         public StringBuffer appendTo(StringBuffer sb) {...} // Formats.
24   *         public String toString() {
25   *             return appendTo(new StringBuffer()).toString();
26   *         }
27   *     }
28   * </pre>
29   * <p>
30   * <i> This class is <b>public domain</b> (not copyrighted).</i>
31   * </p>
32   *
33   * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
34   * @version 4.6, June 22, 2003
35   */
36  public final class TypeFormat
37  {
38  
39      /**
40       * Holds the characters used to represent numbers.
41       */
42      private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
43          'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
44  
45      /**
46       * Default constructor (forbids derivation).
47       */
48      private TypeFormat()
49      {
50      }
51  
52      /**
53       * Searches for a particular sequence within a character sequence (general purpose parsing function).
54       *
55       * @param pattern the character sequence to search for.
56       * @param chars the character sequence being searched.
57       * @param fromIndex the index in <code>chars</code> to start the search from.
58       * @return the index in the range <code>[fromIndex, chars.length()-pattern.length()]</code> or <code>-1</code> if
59       *         the character sequence is not found.
60       */
61      public static int indexOf( CharSequence pattern, CharSequence chars, int fromIndex )
62      {
63          int patternLength = pattern.length();
64          fromIndex = Math.max( 0, fromIndex );
65          if ( patternLength != 0 )
66          { // At least one character to search for.
67              char firstChar = pattern.charAt( 0 );
68              int last = chars.length() - patternLength;
69              for ( int i = fromIndex; i <= last; i++ )
70              {
71                  if ( chars.charAt( i ) == firstChar )
72                  {
73                      boolean match = true;
74                      for ( int j = 1; j < patternLength; j++ )
75                      {
76                          if ( chars.charAt( i + j ) != pattern.charAt( j ) )
77                          {
78                              match = false;
79                              break;
80                          }
81                      }
82                      if ( match )
83                      {
84                          return i;
85                      }
86                  }
87              }
88              return -1;
89          }
90          else
91          {
92              return Math.min( 0, fromIndex );
93          }
94      }
95  
96      /**
97       * Parses the specified <code>CharSequence</code> as a <code>boolean</code>.
98       *
99       * @param chars the character sequence to parse.
100      * @return the corresponding <code>boolean</code>.
101      */
102     public static boolean parseBoolean( CharSequence chars )
103     {
104         return ( chars.length() == 4 ) && ( chars.charAt( 0 ) == 't' || chars.charAt( 0 ) == 'T' )
105             && ( chars.charAt( 1 ) == 'r' || chars.charAt( 1 ) == 'R' )
106             && ( chars.charAt( 2 ) == 'u' || chars.charAt( 2 ) == 'U' )
107             && ( chars.charAt( 3 ) == 'e' || chars.charAt( 3 ) == 'E' );
108     }
109 
110     /**
111      * Parses the specified <code>CharSequence</code> as a signed decimal <code>short</code>.
112      *
113      * @param chars the character sequence to parse.
114      * @return <code>parseShort(chars, 10)</code>
115      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>short</code>.
116      * @see #parseShort(CharSequence, int)
117      */
118     public static short parseShort( CharSequence chars )
119     {
120         return parseShort( chars, 10 );
121     }
122 
123     /**
124      * Parses the specified <code>CharSequence</code> as a signed <code>short</code> in the specified radix. The
125      * characters in the string must all be digits of the specified radix, except the first character which may be a
126      * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
127      *
128      * @param chars the character sequence to parse.
129      * @param radix the radix to be used while parsing.
130      * @return the corresponding <code>short</code>.
131      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>short</code>.
132      */
133     public static short parseShort( CharSequence chars, int radix )
134     {
135         try
136         {
137             boolean isNegative = ( chars.charAt( 0 ) == '-' ) ? true : false;
138             int result = 0;
139             int limit = ( isNegative ) ? Short.MIN_VALUE : -Short.MAX_VALUE;
140             int multmin = limit / radix;
141             int length = chars.length();
142             int i = ( isNegative || ( chars.charAt( 0 ) == '+' ) ) ? 1 : 0;
143             while ( true )
144             {
145                 int digit = Character.digit( chars.charAt( i ), radix );
146                 int tmp = result * radix;
147                 if ( ( digit < 0 ) || ( result < multmin ) || ( tmp < limit + digit ) )
148                 { // Overflow.
149                     throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
150                 }
151                 // Accumulates negatively.
152                 result = tmp - digit;
153                 if ( ++i >= length )
154                 {
155                     break;
156                 }
157             }
158             return (short) ( isNegative ? result : -result );
159         }
160         catch ( IndexOutOfBoundsException e )
161         {
162             throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
163         }
164     }
165 
166     /**
167      * Parses the specified <code>CharSequence</code> as a signed decimal <code>int</code>.
168      *
169      * @param chars the character sequence to parse.
170      * @return <code>parseInt(chars, 10)</code>
171      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>int</code>.
172      * @see #parseInt(CharSequence, int)
173      */
174     public static int parseInt( CharSequence chars )
175     {
176         return parseInt( chars, 10 );
177     }
178 
179     /**
180      * Parses the specified <code>CharSequence</code> as a signed <code>int</code> in the specified radix. The
181      * characters in the string must all be digits of the specified radix, except the first character which may be a
182      * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
183      *
184      * @param chars the character sequence to parse.
185      * @param radix the radix to be used while parsing.
186      * @return the corresponding <code>int</code>.
187      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>int</code>.
188      */
189     public static int parseInt( CharSequence chars, int radix )
190     {
191         try
192         {
193             boolean isNegative = ( chars.charAt( 0 ) == '-' ) ? true : false;
194             int result = 0;
195             int limit = ( isNegative ) ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
196             int multmin = limit / radix;
197             int length = chars.length();
198             int i = ( isNegative || ( chars.charAt( 0 ) == '+' ) ) ? 1 : 0;
199             while ( true )
200             {
201                 int digit = Character.digit( chars.charAt( i ), radix );
202                 int tmp = result * radix;
203                 if ( ( digit < 0 ) || ( result < multmin ) || ( tmp < limit + digit ) )
204                 { // Overflow.
205                     throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
206                 }
207                 // Accumulates negatively to avoid surprises near MAX_VALUE
208                 result = tmp - digit;
209                 if ( ++i >= length )
210                 {
211                     break;
212                 }
213             }
214             return isNegative ? result : -result;
215         }
216         catch ( IndexOutOfBoundsException e )
217         {
218             throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
219         }
220     }
221 
222     /**
223      * Parses the specified <code>CharSequence</code> as a signed decimal <code>long</code>.
224      *
225      * @param chars the character sequence to parse.
226      * @return <code>parseLong(chars, 10)</code>
227      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>long</code>.
228      * @see #parseLong(CharSequence, int)
229      */
230     public static long parseLong( CharSequence chars )
231     {
232         return parseLong( chars, 10 );
233     }
234 
235     /**
236      * Parses the specified <code>CharSequence</code> as a signed <code>long</code> in the specified radix. The
237      * characters in the string must all be digits of the specified radix, except the first character which may be a
238      * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
239      *
240      * @param chars the character sequence to parse.
241      * @param radix the radix to be used while parsing.
242      * @return the corresponding <code>long</code>.
243      * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>long</code>.
244      */
245     public static long parseLong( CharSequence chars, int radix )
246     {
247         try
248         {
249             boolean isNegative = ( chars.charAt( 0 ) == '-' ) ? true : false;
250             long result = 0;
251             long limit = ( isNegative ) ? Long.MIN_VALUE : -Long.MAX_VALUE;
252             long multmin = limit / radix;
253             int length = chars.length();
254             int i = ( isNegative || ( chars.charAt( 0 ) == '+' ) ) ? 1 : 0;
255             while ( true )
256             {
257                 int digit = Character.digit( chars.charAt( i ), radix );
258                 long tmp = result * radix;
259                 if ( ( digit < 0 ) || ( result < multmin ) || ( tmp < limit + digit ) )
260                 { // Overflow.
261                     throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
262                 }
263                 // Accumulates negatively to avoid surprises near MAX_VALUE
264                 result = tmp - digit;
265                 if ( ++i >= length )
266                 {
267                     break;
268                 }
269             }
270             return isNegative ? result : -result;
271         }
272         catch ( IndexOutOfBoundsException e )
273         {
274             throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
275         }
276     }
277 
278     /**
279      * Parses this <code>CharSequence</code> as a <code>float</code>.
280      *
281      * @param chars the character sequence to parse.
282      * @return the float number represented by the specified character sequence.
283      * @throws NumberFormatException if the character sequence does not contain a parsable <code>float</code>.
284      */
285     public static float parseFloat( CharSequence chars )
286     {
287         double d = parseDouble( chars );
288         if ( ( d >= Float.MIN_VALUE ) && ( d <= Float.MAX_VALUE ) )
289         {
290             return (float) d;
291         }
292         else
293         {
294             throw new NumberFormatException( "Float overflow for input characters: \"" + chars.toString() + "\"" );
295         }
296     }
297 
298     /**
299      * Parses this <code>CharSequence</code> as a <code>double</code>.
300      *
301      * @param chars the character sequence to parse.
302      * @return the double number represented by this character sequence.
303      * @throws NumberFormatException if the character sequence does not contain a parsable <code>double</code>.
304      */
305     public static double parseDouble( CharSequence chars )
306         throws NumberFormatException
307     {
308         try
309         {
310             int length = chars.length();
311             double result = 0.0;
312             int exp = 0;
313 
314             boolean isNegative = ( chars.charAt( 0 ) == '-' ) ? true : false;
315             int i = ( isNegative || ( chars.charAt( 0 ) == '+' ) ) ? 1 : 0;
316 
317             // Checks special cases NaN or Infinity.
318             if ( ( chars.charAt( i ) == 'N' ) || ( chars.charAt( i ) == 'I' ) )
319             {
320                 if ( chars.toString().equals( "NaN" ) )
321                 {
322                     return Double.NaN;
323                 }
324                 else if ( chars.subSequence( i, length ).toString().equals( "Infinity" ) )
325                 {
326                     return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
327                 }
328             }
329 
330             // Reads decimal number.
331             boolean fraction = false;
332             while ( true )
333             {
334                 char c = chars.charAt( i );
335                 if ( ( c == '.' ) && ( !fraction ) )
336                 {
337                     fraction = true;
338                 }
339                 else if ( ( c == 'e' ) || ( c == 'E' ) )
340                 {
341                     break;
342                 }
343                 else if ( ( c >= '0' ) && ( c <= '9' ) )
344                 {
345                     result = result * 10 + ( c - '0' );
346                     if ( fraction )
347                     {
348                         exp--;
349                     }
350                 }
351                 else
352                 {
353                     throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
354                 }
355                 if ( ++i >= length )
356                 {
357                     break;
358                 }
359             }
360             result = isNegative ? -result : result;
361 
362             // Reads exponent (if any).
363             if ( i < length )
364             {
365                 i++;
366                 boolean negE = ( chars.charAt( i ) == '-' ) ? true : false;
367                 i = ( negE || ( chars.charAt( i ) == '+' ) ) ? i + 1 : i;
368                 int valE = 0;
369                 while ( true )
370                 {
371                     char c = chars.charAt( i );
372                     if ( ( c >= '0' ) && ( c <= '9' ) )
373                     {
374                         valE = valE * 10 + ( c - '0' );
375                         if ( valE > 10000000 )
376                         { // Hard-limit to avoid overflow.
377                             valE = 10000000;
378                         }
379                     }
380                     else
381                     {
382                         throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
383                     }
384                     if ( ++i >= length )
385                     {
386                         break;
387                     }
388                 }
389                 exp += negE ? -valE : valE;
390             }
391 
392             // Returns product decimal number with exponent.
393             return multE( result, exp );
394 
395         }
396         catch ( IndexOutOfBoundsException e )
397         {
398             throw new NumberFormatException( "For input characters: \"" + chars.toString() + "\"" );
399         }
400     }
401 
402     /**
403      * Formats the specified <code>boolean</code> and appends the resulting text to the <code>StringBuffer</code>
404      * argument.
405      *
406      * @param b a <code>boolean</code>.
407      * @param sb the <code>StringBuffer</code> to append.
408      * @return the specified <code>StringBuffer</code> object.
409      * @see #parseBoolean
410      */
411     public static StringBuffer format( boolean b, StringBuffer sb )
412     {
413         return b ? sb.append( "true" ) : sb.append( "false" );
414     }
415 
416     /**
417      * Formats the specified <code>short</code> and appends the resulting text (decimal representation) to the
418      * <code>StringBuffer</code> argument.
419      * <p>
420      * Note: This method is preferred to <code>StringBuffer.append(short)
421      *           </code> as it does not create temporary <code>String</code> objects (several times faster for small
422      * numbers).
423      * </p>
424      *
425      * @param s the <code>short</code> number.
426      * @param sb the <code>StringBuffer</code> to append.
427      * @return the specified <code>StringBuffer</code> object.
428      * @see #parseShort
429      */
430     public static StringBuffer format( short s, StringBuffer sb )
431     {
432         return format( (int) s, sb ); // Forwards to int formatting (fast).
433     }
434 
435     /**
436      * Formats the specified <code>short</code> in the specified radix and appends the resulting text to the
437      * <code>StringBuffer</code> argument.
438      *
439      * @param s the <code>short</code> number.
440      * @param radix the radix.
441      * @param sb the <code>StringBuffer</code> to append.
442      * @return the specified <code>StringBuffer</code> object.
443      * @see #parseShort(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
444      */
445     public static StringBuffer format( short s, int radix, StringBuffer sb )
446     {
447         return format( (int) s, radix, sb ); // Forwards to int formatting (fast).
448     }
449 
450     /**
451      * Formats the specified <code>int</code> and appends the resulting text (decimal representation) to the
452      * <code>StringBuffer</code> argument.
453      * <p>
454      * Note: This method is preferred to <code>StringBuffer.append(int)
455      *           </code> as it does not create temporary <code>String</code> objects (several times faster for small
456      * numbers).
457      * </p>
458      *
459      * @param i the <code>int</code> number.
460      * @param sb the <code>StringBuffer</code> to append.
461      * @return the specified <code>StringBuffer</code> object.
462      * @see #parseInt
463      */
464     public static StringBuffer format( int i, StringBuffer sb )
465     {
466         if ( i <= 0 )
467         {
468             if ( i == Integer.MIN_VALUE )
469             { // Negation would overflow.
470                 return sb.append( "-2147483648" ); // 11 char max.
471             }
472             else if ( i == 0 )
473             {
474                 return sb.append( '0' );
475             }
476             i = -i;
477             sb.append( '-' );
478         }
479         int j = 1;
480         for ( ; ( j < 10 ) && ( i >= INT_POW_10[j] ); j++ )
481         {
482         }
483         // POW_10[j] > i >= POW_10[j-1]
484         for ( j--; j >= 0; j-- )
485         {
486             int pow10 = INT_POW_10[j];
487             int digit = i / pow10;
488             i -= digit * pow10;
489             sb.append( DIGITS[digit] );
490         }
491         return sb;
492     }
493 
494     private static final int[] INT_POW_10 = new int[10];
495     static
496     {
497         int pow = 1;
498         for ( int i = 0; i < 10; i++ )
499         {
500             INT_POW_10[i] = pow;
501             pow *= 10;
502         }
503     }
504 
505     /**
506      * Formats the specified <code>int</code> in the specified radix and appends the resulting text to the
507      * <code>StringBuffer</code> argument.
508      *
509      * @param i the <code>int</code> number.
510      * @param radix the radix.
511      * @param sb the <code>StringBuffer</code> to append.
512      * @return the specified <code>StringBuffer</code> object.
513      * @see #parseInt(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
514      */
515     public static StringBuffer format( int i, int radix, StringBuffer sb )
516     {
517         if ( radix == 10 )
518         {
519             return format( i, sb ); // Faster version.
520         }
521         else if ( radix < 2 || radix > 36 )
522         {
523             throw new IllegalArgumentException( "radix: " + radix );
524         }
525         if ( i < 0 )
526         {
527             sb.append( '-' );
528         }
529         else
530         {
531             i = -i;
532         }
533         format2( i, radix, sb );
534         return sb;
535     }
536 
537     private static void format2( int i, int radix, StringBuffer sb )
538     {
539         if ( i <= -radix )
540         {
541             format2( i / radix, radix, sb );
542             sb.append( DIGITS[-( i % radix )] );
543         }
544         else
545         {
546             sb.append( DIGITS[-i] );
547         }
548     }
549 
550     /**
551      * Formats the specified <code>long</code> and appends the resulting text (decimal representation) to the
552      * <code>StringBuffer</code> argument.
553      * <p>
554      * Note: This method is preferred to <code>StringBuffer.append(long)
555      *           </code> as it does not create temporary <code>String</code> objects (several times faster for small
556      * numbers).
557      * </p>
558      *
559      * @param l the <code>long</code> number.
560      * @param sb the <code>StringBuffer</code> to append.
561      * @return the specified <code>StringBuffer</code> object.
562      * @see #parseLong
563      */
564     public static StringBuffer format( long l, StringBuffer sb )
565     {
566         if ( l <= 0 )
567         {
568             if ( l == Long.MIN_VALUE )
569             { // Negation would overflow.
570                 return sb.append( "-9223372036854775808" ); // 20 characters max.
571             }
572             else if ( l == 0 )
573             {
574                 return sb.append( '0' );
575             }
576             l = -l;
577             sb.append( '-' );
578         }
579         int j = 1;
580         for ( ; ( j < 19 ) && ( l >= LONG_POW_10[j] ); j++ )
581         {
582         }
583         // POW_10[j] > l >= POW_10[j-1]
584         for ( j--; j >= 0; j-- )
585         {
586             long pow10 = LONG_POW_10[j];
587             int digit = (int) ( l / pow10 );
588             l -= digit * pow10;
589             sb.append( DIGITS[digit] );
590         }
591         return sb;
592     }
593 
594     private static final long[] LONG_POW_10 = new long[19];
595     static
596     {
597         long pow = 1;
598         for ( int i = 0; i < 19; i++ )
599         {
600             LONG_POW_10[i] = pow;
601             pow *= 10;
602         }
603     }
604 
605     /**
606      * Formats the specified <code>long</code> in the specified radix and appends the resulting text to the
607      * <code>StringBuffer</code> argument.
608      *
609      * @param l the <code>long</code> number.
610      * @param radix the radix.
611      * @param sb the <code>StringBuffer</code> to append.
612      * @return the specified <code>StringBuffer</code> object.
613      * @see #parseLong(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
614      */
615     public static StringBuffer format( long l, int radix, StringBuffer sb )
616     {
617         if ( radix == 10 )
618         {
619             return format( l, sb ); // Faster version.
620         }
621         else if ( radix < 2 || radix > 36 )
622         {
623             throw new IllegalArgumentException( "radix: " + radix );
624         }
625         if ( l < 0 )
626         {
627             sb.append( '-' );
628         }
629         else
630         {
631             l = -l;
632         }
633         format2( l, radix, sb );
634         return sb;
635     }
636 
637     private static void format2( long l, int radix, StringBuffer sb )
638     {
639         if ( l <= -radix )
640         {
641             format2( l / radix, radix, sb );
642             sb.append( DIGITS[(int) -( l % radix )] );
643         }
644         else
645         {
646             sb.append( DIGITS[(int) -l] );
647         }
648     }
649 
650     /**
651      * Formats the specified <code>float</code> and appends the resulting text to the <code>StringBuffer</code>
652      * argument.
653      *
654      * @param f the <code>float</code> number.
655      * @param sb the <code>StringBuffer</code> to append.
656      * @return <code>format(f, 0.0f, sb)</code>
657      * @see #format(float, float, StringBuffer)
658      */
659     public static StringBuffer format( float f, StringBuffer sb )
660     {
661         return format( f, 0.0f, sb );
662     }
663 
664     /**
665      * Formats the specified <code>float</code> and appends the resulting text to the <code>StringBuffer</code>
666      * argument; the number of significative digits is deduced from the specified precision. All digits at least as
667      * significant as the specified precision are represented. For example:
668      * <ul>
669      * <li><code>format(5.6f, 0.01f, sb)</code> appends <code>"5.60"</code></li>
670      * <li><code>format(5.6f, 0.1f, sb)</code> appends <code>"5.6"</code></li>
671      * <li><code>format(5.6f, 1f, sb)</code> appends <code>"6"</code></li>
672      * </ul>
673      * If the precision is <code>0.0f</code>, the precision is assumed to be the intrinsic <code>float</code> precision
674      * (64 bits IEEE 754 format); no formatting is performed, all significant digits are displayed and trailing zeros
675      * are removed.
676      *
677      * @param f the <code>float</code> number.
678      * @param precision the maximum weight of the last digit represented.
679      * @param sb the <code>StringBuffer</code> to append.
680      * @return the specified <code>StringBuffer</code> object.
681      * @throws IllegalArgumentException if the specified precision is negative or would result in too many digits (19+).
682      */
683     public static StringBuffer format( float f, float precision, StringBuffer sb )
684     {
685         // Adjusts precision.
686         boolean precisionOnLastDigit;
687         if ( precision > 0.0f )
688         {
689             precisionOnLastDigit = true;
690         }
691         else if ( precision == 0.0f )
692         {
693             if ( f != 0.0f )
694             {
695                 precisionOnLastDigit = false;
696                 precision = Math.max( Math.abs( f * FLOAT_RELATIVE_ERROR ), Float.MIN_VALUE );
697             }
698             else
699             {
700                 return sb.append( "0.0" ); // Exact zero.
701             }
702         }
703         else
704         {
705             throw new IllegalArgumentException( "precision: Negative values not allowed" );
706         }
707         return format( f, precision, precisionOnLastDigit, sb );
708     }
709 
710     /**
711      * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
712      * argument.
713      * <p>
714      * Note : This method is preferred to <code>StringBuffer.append(double)
715      *            </code> or even <code>String.valueOf(double)</code> as it does not create temporary
716      * <code>String</code> or <code>
717      *            FloatingDecimal</code> objects (several times faster, e.g. 15x faster for
718      * <code>Double.MAX_VALUE</code>).
719      * </p>
720      *
721      * @param d the <code>double</code> number.
722      * @param sb the <code>StringBuffer</code> to append.
723      * @return <code>format(d, 0.0, sb)</code>
724      * @see #format(double, double, StringBuffer)
725      */
726     public static StringBuffer format( double d, StringBuffer sb )
727     {
728         return format( d, 0.0, sb );
729     }
730 
731     /**
732      * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
733      * argument; the number of significand digits is specified as integer argument.
734      *
735      * @param d the <code>double</code> number.
736      * @param digits the number of significand digits (excludes exponent).
737      * @param sb the <code>StringBuffer</code> to append.
738      * @return the specified <code>StringBuffer</code> object.
739      * @throws IllegalArgumentException if the number of digits is not in range <code>[1..19]</code>.
740      */
741     public static StringBuffer format( double d, int digits, StringBuffer sb )
742     {
743         if ( ( digits >= 1 ) && ( digits <= 19 ) )
744         {
745             double precision = Math.abs( d / DOUBLE_POW_10[digits - 1] );
746             return format( d, precision, sb );
747         }
748         else
749         {
750             throw new java.lang.IllegalArgumentException( "digits: " + digits + " is not in range [1 .. 19]" );
751         }
752     }
753 
754     /**
755      * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
756      * argument; the number of significative digits is deduced from the specified precision. All digits at least as
757      * significant as the specified precision are represented. For example:
758      * <ul>
759      * <li><code>format(5.6, 0.01, sb)</code> appends <code>"5.60"</code></li>
760      * <li><code>format(5.6, 0.1, sb)</code> appends <code>"5.6"</code></li>
761      * <li><code>format(5.6, 1, sb)</code> appends <code>"6"</code></li>
762      * </ul>
763      * If the precision is <code>0.0</code>, the precision is assumed to be the intrinsic <code>double</code> precision
764      * (64 bits IEEE 754 format); no formatting is performed, all significant digits are displayed and trailing zeros
765      * are removed.
766      *
767      * @param d the <code>double</code> number.
768      * @param precision the maximum weight of the last digit represented.
769      * @param sb the <code>StringBuffer</code> to append.
770      * @return the specified <code>StringBuffer</code> object.
771      * @throws IllegalArgumentException if the specified precision is negative or would result in too many digits (19+).
772      */
773     public static StringBuffer format( double d, double precision, StringBuffer sb )
774     {
775         // Adjusts precision.
776         boolean precisionOnLastDigit = false;
777         if ( precision > 0.0 )
778         {
779             precisionOnLastDigit = true;
780         }
781         else if ( precision == 0.0 )
782         {
783             if ( d != 0.0 )
784             {
785                 precision = Math.max( Math.abs( d * DOUBLE_RELATIVE_ERROR ), Double.MIN_VALUE );
786             }
787             else
788             {
789                 return sb.append( "0.0" ); // Exact zero.
790             }
791         }
792         else if ( precision < 0.0 )
793         { // Not NaN
794             throw new IllegalArgumentException( "precision: Negative values not allowed" );
795         }
796         return format( d, precision, precisionOnLastDigit, sb );
797     }
798 
799     /**
800      * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
801      * argument; the number of significative digits is deduced from the specified precision.
802      *
803      * @param d the <code>double</code> number.
804      * @param precision the maximum weight of the last digit represented.
805      * @param precisionOnLastDigit indicates if the number of digits is deduced from the specified precision.
806      * @param sb the <code>StringBuffer</code> to append.
807      * @return the specified <code>StringBuffer</code> object.
808      */
809     private static StringBuffer format( double d, double precision, boolean precisionOnLastDigit, StringBuffer sb )
810     {
811         // Special cases.
812         if ( Double.isNaN( d ) )
813         {
814             return sb.append( "NaN" );
815         }
816         else if ( Double.isInfinite( d ) )
817         {
818             return ( d >= 0 ) ? sb.append( "Infinity" ) : sb.append( "-Infinity" );
819         }
820         if ( d < 0 )
821         {
822             d = -d;
823             sb.append( '-' );
824         }
825 
826         // Formats decimal part.
827         int rank = (int) Math.floor( Math.log( precision ) / LOG_10 );
828         double digitValue = multE( d, -rank );
829         if ( digitValue >= Long.MAX_VALUE )
830         {
831             throw new IllegalArgumentException( "Specified precision would result in too many digits" );
832         }
833         int digitStart = sb.length();
834         format( Math.round( digitValue ), sb );
835         int digitLength = sb.length() - digitStart;
836         int dotPos = digitLength + rank;
837         boolean useScientificNotation = false;
838 
839         // Inserts dot ('.')
840         if ( ( dotPos <= -LEADING_ZEROS.length ) || ( dotPos > digitLength ) )
841         {
842             // Scientific notation has to be used ("x.xxxEyy").
843             sb.insert( digitStart + 1, '.' );
844             useScientificNotation = true;
845         }
846         else if ( dotPos > 0 )
847         {
848             // Dot within the string ("xxxx.xxxxx").
849             sb.insert( digitStart + dotPos, '.' );
850         }
851         else
852         {
853             // Leading zeros ("0.xxxxx").
854             sb.insert( digitStart, LEADING_ZEROS[-dotPos] );
855         }
856 
857         // Removes trailing zeros.
858         if ( !precisionOnLastDigit )
859         {
860             int newLength = sb.length();
861             do
862             {
863                 newLength--;
864             }
865             while ( sb.charAt( newLength ) == '0' );
866             sb.setLength( newLength + 1 );
867         }
868 
869         // Avoids trailing '.'
870         if ( sb.charAt( sb.length() - 1 ) == '.' )
871         {
872             if ( precisionOnLastDigit )
873             {
874                 sb.setLength( sb.length() - 1 ); // Prefers "xxx" to "xxx."
875             }
876             else
877             {
878                 sb.append( '0' ); // Prefer "xxx.0" to "xxx."
879             }
880         }
881 
882         // Writes exponent.
883         if ( useScientificNotation )
884         {
885             sb.append( 'E' );
886             format( dotPos - 1, sb );
887         }
888 
889         return sb;
890     }
891 
892     private static final double LOG_10 = Math.log( 10 );
893 
894     private static final float FLOAT_RELATIVE_ERROR = (float) Math.pow( 2, -24 );
895 
896     private static final double DOUBLE_RELATIVE_ERROR = Math.pow( 2, -53 );
897 
898     private static String[] LEADING_ZEROS = { "0.", "0.0", "0.00" };
899 
900     /**
901      * Returns the product of the specified value with <code>10</code> raised at the specified power exponent.
902      *
903      * @param value the value.
904      * @param E the exponent.
905      * @return <code>value * 10^E</code>
906      */
907     private static final double multE( double value, int E )
908     {
909         if ( E >= 0 )
910         {
911             if ( E <= 308 )
912             {
913                 // Max: 1.7976931348623157E+308
914                 return value * DOUBLE_POW_10[E];
915             }
916             else
917             {
918                 value *= 1E21; // Exact multiplicand.
919                 E = Math.min( 308, E - 21 );
920                 return value * DOUBLE_POW_10[E];
921             }
922         }
923         else
924         {
925             if ( E >= -308 )
926             {
927                 return value / DOUBLE_POW_10[-E];
928             }
929             else
930             {
931                 // Min: 4.9E-324
932                 value /= 1E21; // Exact divisor.
933                 E = Math.max( -308, E + 21 );
934                 return value / DOUBLE_POW_10[-E];
935             }
936         }
937     }
938 
939     // Note: Approximation for exponents > 21. This may introduce round-off
940     // errors (e.g. 1E23 represented as "9.999999999999999E22").
941     private static final double[] DOUBLE_POW_10 = new double[] {
942 
943         1E000, 1E001, 1E002, 1E003, 1E004, 1E005, 1E006, 1E007, 1E008, 1E009, 1E010, 1E011, 1E012, 1E013, 1E014, 1E015,
944         1E016, 1E017, 1E018, 1E019, 1E020, 1E021, 1E022, 1E023, 1E024, 1E025, 1E026, 1E027, 1E028, 1E029, 1E030, 1E031,
945         1E032, 1E033, 1E034, 1E035, 1E036, 1E037, 1E038, 1E039, 1E040, 1E041, 1E042, 1E043, 1E044, 1E045, 1E046, 1E047,
946         1E048, 1E049, 1E050, 1E051, 1E052, 1E053, 1E054, 1E055, 1E056, 1E057, 1E058, 1E059, 1E060, 1E061, 1E062, 1E063,
947         1E064, 1E065, 1E066, 1E067, 1E068, 1E069, 1E070, 1E071, 1E072, 1E073, 1E074, 1E075, 1E076, 1E077, 1E078, 1E079,
948         1E080, 1E081, 1E082, 1E083, 1E084, 1E085, 1E086, 1E087, 1E088, 1E089, 1E090, 1E091, 1E092, 1E093, 1E094, 1E095,
949         1E096, 1E097, 1E098, 1E099,
950 
951         1E100, 1E101, 1E102, 1E103, 1E104, 1E105, 1E106, 1E107, 1E108, 1E109, 1E110, 1E111, 1E112, 1E113, 1E114, 1E115,
952         1E116, 1E117, 1E118, 1E119, 1E120, 1E121, 1E122, 1E123, 1E124, 1E125, 1E126, 1E127, 1E128, 1E129, 1E130, 1E131,
953         1E132, 1E133, 1E134, 1E135, 1E136, 1E137, 1E138, 1E139, 1E140, 1E141, 1E142, 1E143, 1E144, 1E145, 1E146, 1E147,
954         1E148, 1E149, 1E150, 1E151, 1E152, 1E153, 1E154, 1E155, 1E156, 1E157, 1E158, 1E159, 1E160, 1E161, 1E162, 1E163,
955         1E164, 1E165, 1E166, 1E167, 1E168, 1E169, 1E170, 1E171, 1E172, 1E173, 1E174, 1E175, 1E176, 1E177, 1E178, 1E179,
956         1E180, 1E181, 1E182, 1E183, 1E184, 1E185, 1E186, 1E187, 1E188, 1E189, 1E190, 1E191, 1E192, 1E193, 1E194, 1E195,
957         1E196, 1E197, 1E198, 1E199,
958 
959         1E200, 1E201, 1E202, 1E203, 1E204, 1E205, 1E206, 1E207, 1E208, 1E209, 1E210, 1E211, 1E212, 1E213, 1E214, 1E215,
960         1E216, 1E217, 1E218, 1E219, 1E220, 1E221, 1E222, 1E223, 1E224, 1E225, 1E226, 1E227, 1E228, 1E229, 1E230, 1E231,
961         1E232, 1E233, 1E234, 1E235, 1E236, 1E237, 1E238, 1E239, 1E240, 1E241, 1E242, 1E243, 1E244, 1E245, 1E246, 1E247,
962         1E248, 1E249, 1E250, 1E251, 1E252, 1E253, 1E254, 1E255, 1E256, 1E257, 1E258, 1E259, 1E260, 1E261, 1E262, 1E263,
963         1E264, 1E265, 1E266, 1E267, 1E268, 1E269, 1E270, 1E271, 1E272, 1E273, 1E274, 1E275, 1E276, 1E277, 1E278, 1E279,
964         1E280, 1E281, 1E282, 1E283, 1E284, 1E285, 1E286, 1E287, 1E288, 1E289, 1E290, 1E291, 1E292, 1E293, 1E294, 1E295,
965         1E296, 1E297, 1E298, 1E299,
966 
967         1E300, 1E301, 1E302, 1E303, 1E304, 1E305, 1E306, 1E307, 1E308 };
968 }