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 | } |