View Javadoc

1   /*
2    *  Copyright 2001-2005 Stephen Colebourne
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  package org.joda.time.format;
17  
18  import java.io.IOException;
19  import java.io.Writer;
20  
21  /**
22   * Utility methods used by formatters.
23   * <p>
24   * FormatUtils is thread-safe and immutable.
25   *
26   * @author Brian S O'Neill
27   * @since 1.0
28   */
29  public class FormatUtils {
30  
31      private static final double LOG_10 = Math.log(10);
32  
33      /**
34       * Restricted constructor.
35       */
36      private FormatUtils() {
37      }
38  
39      /**
40       * Converts an integer to a string, prepended with a variable amount of '0'
41       * pad characters, and appends it to the given buffer.
42       *
43       * <p>This method is optimized for converting small values to strings.
44       *
45       * @param buf receives integer converted to a string
46       * @param value value to convert to a string
47       * @param size minumum amount of digits to append
48       */
49      public static void appendPaddedInteger(StringBuffer buf, int value, int size) {
50          if (value < 0) {
51              buf.append('-');
52              if (value != Integer.MIN_VALUE) {
53                  value = -value;
54              } else {
55                  for (; size > 10; size--) {
56                      buf.append('0');
57                  }
58                  buf.append("" + -(long)Integer.MIN_VALUE);
59                  return;
60              }
61          }
62          if (value < 10) {
63              for (; size > 1; size--) {
64                  buf.append('0');
65              }
66              buf.append((char)(value + '0'));
67          } else if (value < 100) {
68              for (; size > 2; size--) {
69                  buf.append('0');
70              }
71              // Calculate value div/mod by 10 without using two expensive
72              // division operations. (2 ^ 27) / 10 = 13421772. Add one to
73              // value to correct rounding error.
74              int d = ((value + 1) * 13421772) >> 27;
75              buf.append((char) (d + '0'));
76              // Append remainder by calculating (value - d * 10).
77              buf.append((char) (value - (d << 3) - (d << 1) + '0'));
78          } else {
79              int digits;
80              if (value < 1000) {
81                  digits = 3;
82              } else if (value < 10000) {
83                  digits = 4;
84              } else {
85                  digits = (int)(Math.log(value) / LOG_10) + 1;
86              }
87              for (; size > digits; size--) {
88                  buf.append('0');
89              }
90              buf.append(Integer.toString(value));
91          }
92      }
93  
94      /**
95       * Converts an integer to a string, prepended with a variable amount of '0'
96       * pad characters, and appends it to the given buffer.
97       *
98       * <p>This method is optimized for converting small values to strings.
99       *
100      * @param buf receives integer converted to a string
101      * @param value value to convert to a string
102      * @param size minumum amount of digits to append
103      */
104     public static void appendPaddedInteger(StringBuffer buf, long value, int size) {
105         int intValue = (int)value;
106         if (intValue == value) {
107             appendPaddedInteger(buf, intValue, size);
108         } else if (size <= 19) {
109             buf.append(Long.toString(value));
110         } else {
111             if (value < 0) {
112                 buf.append('-');
113                 if (value != Long.MIN_VALUE) {
114                     value = -value;
115                 } else {
116                     for (; size > 19; size--) {
117                         buf.append('0');
118                     }
119                     buf.append("9223372036854775808");
120                     return;
121                 }
122             }
123             int digits = (int)(Math.log(value) / LOG_10) + 1;
124             for (; size > digits; size--) {
125                 buf.append('0');
126             }
127             buf.append(Long.toString(value));
128         }
129     }
130 
131     /**
132      * Converts an integer to a string, prepended with a variable amount of '0'
133      * pad characters, and writes it to the given writer.
134      *
135      * <p>This method is optimized for converting small values to strings.
136      *
137      * @param out receives integer converted to a string
138      * @param value value to convert to a string
139      * @param size minumum amount of digits to append
140      */
141     public static void writePaddedInteger(Writer out, int value, int size)
142         throws IOException
143     {
144         if (value < 0) {
145             out.write('-');
146             if (value != Integer.MIN_VALUE) {
147                 value = -value;
148             } else {
149                 for (; size > 10; size--) {
150                     out.write('0');
151                 }
152                 out.write("" + -(long)Integer.MIN_VALUE);
153                 return;
154             }
155         }
156         if (value < 10) {
157             for (; size > 1; size--) {
158                 out.write('0');
159             }
160             out.write(value + '0');
161         } else if (value < 100) {
162             for (; size > 2; size--) {
163                 out.write('0');
164             }
165             // Calculate value div/mod by 10 without using two expensive
166             // division operations. (2 ^ 27) / 10 = 13421772. Add one to
167             // value to correct rounding error.
168             int d = ((value + 1) * 13421772) >> 27;
169             out.write(d + '0');
170             // Append remainder by calculating (value - d * 10).
171             out.write(value - (d << 3) - (d << 1) + '0');
172         } else {
173             int digits;
174             if (value < 1000) {
175                 digits = 3;
176             } else if (value < 10000) {
177                 digits = 4;
178             } else {
179                 digits = (int)(Math.log(value) / LOG_10) + 1;
180             }
181             for (; size > digits; size--) {
182                 out.write('0');
183             }
184             out.write(Integer.toString(value));
185         }
186     }
187 
188     /**
189      * Converts an integer to a string, prepended with a variable amount of '0'
190      * pad characters, and writes it to the given writer.
191      *
192      * <p>This method is optimized for converting small values to strings.
193      *
194      * @param out receives integer converted to a string
195      * @param value value to convert to a string
196      * @param size minumum amount of digits to append
197      */
198     public static void writePaddedInteger(Writer out, long value, int size)
199         throws IOException
200     {
201         int intValue = (int)value;
202         if (intValue == value) {
203             writePaddedInteger(out, intValue, size);
204         } else if (size <= 19) {
205             out.write(Long.toString(value));
206         } else {
207             if (value < 0) {
208                 out.write('-');
209                 if (value != Long.MIN_VALUE) {
210                     value = -value;
211                 } else {
212                     for (; size > 19; size--) {
213                         out.write('0');
214                     }
215                     out.write("9223372036854775808");
216                     return;
217                 }
218             }
219             int digits = (int)(Math.log(value) / LOG_10) + 1;
220             for (; size > digits; size--) {
221                 out.write('0');
222             }
223             out.write(Long.toString(value));
224         }
225     }
226 
227     /**
228      * Converts an integer to a string, and appends it to the given buffer.
229      *
230      * <p>This method is optimized for converting small values to strings.
231      *
232      * @param buf receives integer converted to a string
233      * @param value value to convert to a string
234      */
235     public static void appendUnpaddedInteger(StringBuffer buf, int value) {
236         if (value < 0) {
237             buf.append('-');
238             if (value != Integer.MIN_VALUE) {
239                 value = -value;
240             } else {
241                 buf.append("" + -(long)Integer.MIN_VALUE);
242                 return;
243             }
244         }
245         if (value < 10) {
246             buf.append((char)(value + '0'));
247         } else if (value < 100) {
248             // Calculate value div/mod by 10 without using two expensive
249             // division operations. (2 ^ 27) / 10 = 13421772. Add one to
250             // value to correct rounding error.
251             int d = ((value + 1) * 13421772) >> 27;
252             buf.append((char) (d + '0'));
253             // Append remainder by calculating (value - d * 10).
254             buf.append((char) (value - (d << 3) - (d << 1) + '0'));
255         } else {
256             buf.append(Integer.toString(value));
257         }
258     }
259 
260     /**
261      * Converts an integer to a string, and appends it to the given buffer.
262      *
263      * <p>This method is optimized for converting small values to strings.
264      *
265      * @param buf receives integer converted to a string
266      * @param value value to convert to a string
267      */
268     public static void appendUnpaddedInteger(StringBuffer buf, long value) {
269         int intValue = (int)value;
270         if (intValue == value) {
271             appendUnpaddedInteger(buf, intValue);
272         } else {
273             buf.append(Long.toString(value));
274         }
275     }
276 
277     /**
278      * Converts an integer to a string, and writes it to the given writer.
279      *
280      * <p>This method is optimized for converting small values to strings.
281      *
282      * @param out receives integer converted to a string
283      * @param value value to convert to a string
284      */
285     public static void writeUnpaddedInteger(Writer out, int value)
286         throws IOException
287     {
288         if (value < 0) {
289             out.write('-');
290             if (value != Integer.MIN_VALUE) {
291                 value = -value;
292             } else {
293                 out.write("" + -(long)Integer.MIN_VALUE);
294                 return;
295             }
296         }
297         if (value < 10) {
298             out.write(value + '0');
299         } else if (value < 100) {
300             // Calculate value div/mod by 10 without using two expensive
301             // division operations. (2 ^ 27) / 10 = 13421772. Add one to
302             // value to correct rounding error.
303             int d = ((value + 1) * 13421772) >> 27;
304             out.write(d + '0');
305             // Append remainder by calculating (value - d * 10).
306             out.write(value - (d << 3) - (d << 1) + '0');
307         } else {
308             out.write(Integer.toString(value));
309         }
310     }
311 
312     /**
313      * Converts an integer to a string, and writes it to the given writer.
314      *
315      * <p>This method is optimized for converting small values to strings.
316      *
317      * @param out receives integer converted to a string
318      * @param value value to convert to a string
319      */
320     public static void writeUnpaddedInteger(Writer out, long value)
321         throws IOException
322     {
323         int intValue = (int)value;
324         if (intValue == value) {
325             writeUnpaddedInteger(out, intValue);
326         } else {
327             out.write(Long.toString(value));
328         }
329     }
330 
331     /**
332      * Calculates the number of decimal digits for the given value,
333      * including the sign.
334      */
335     public static int calculateDigitCount(long value) {
336         if (value < 0) {
337             if (value != Long.MIN_VALUE) {
338                 return calculateDigitCount(-value) + 1;
339             } else {
340                 return 20;
341             }
342         }
343         return 
344             (value < 10 ? 1 :
345              (value < 100 ? 2 :
346               (value < 1000 ? 3 :
347                (value < 10000 ? 4 :
348                 ((int)(Math.log(value) / LOG_10) + 1)))));
349     }
350 
351     static int parseTwoDigits(String text, int position) {
352         int value = text.charAt(position) - '0';
353         return ((value << 3) + (value << 1)) + text.charAt(position + 1) - '0';
354     }
355 
356     static String createErrorMessage(final String text, final int errorPos) {
357         int sampleLen = errorPos + 32;
358         String sampleText;
359         if (text.length() <= sampleLen + 3) {
360             sampleText = text;
361         } else {
362             sampleText = text.substring(0, sampleLen).concat("...");
363         }
364         
365         if (errorPos <= 0) {
366             return "Invalid format: \"" + sampleText + '"';
367         }
368         
369         if (errorPos >= text.length()) {
370             return "Invalid format: \"" + sampleText + "\" is too short";
371         }
372         
373         return "Invalid format: \"" + sampleText + "\" is malformed at \"" +
374             sampleText.substring(errorPos) + '"';
375     }
376 
377 }