001 /*
002 * Copyright 2001-2005 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time.format;
017
018 import java.io.IOException;
019 import java.io.Writer;
020
021 /**
022 * Utility methods used by formatters.
023 * <p>
024 * FormatUtils is thread-safe and immutable.
025 *
026 * @author Brian S O'Neill
027 * @since 1.0
028 */
029 public class FormatUtils {
030
031 private static final double LOG_10 = Math.log(10);
032
033 /**
034 * Restricted constructor.
035 */
036 private FormatUtils() {
037 }
038
039 /**
040 * Converts an integer to a string, prepended with a variable amount of '0'
041 * pad characters, and appends it to the given buffer.
042 *
043 * <p>This method is optimized for converting small values to strings.
044 *
045 * @param buf receives integer converted to a string
046 * @param value value to convert to a string
047 * @param size minumum amount of digits to append
048 */
049 public static void appendPaddedInteger(StringBuffer buf, int value, int size) {
050 if (value < 0) {
051 buf.append('-');
052 if (value != Integer.MIN_VALUE) {
053 value = -value;
054 } else {
055 for (; size > 10; size--) {
056 buf.append('0');
057 }
058 buf.append("" + -(long)Integer.MIN_VALUE);
059 return;
060 }
061 }
062 if (value < 10) {
063 for (; size > 1; size--) {
064 buf.append('0');
065 }
066 buf.append((char)(value + '0'));
067 } else if (value < 100) {
068 for (; size > 2; size--) {
069 buf.append('0');
070 }
071 // Calculate value div/mod by 10 without using two expensive
072 // division operations. (2 ^ 27) / 10 = 13421772. Add one to
073 // value to correct rounding error.
074 int d = ((value + 1) * 13421772) >> 27;
075 buf.append((char) (d + '0'));
076 // Append remainder by calculating (value - d * 10).
077 buf.append((char) (value - (d << 3) - (d << 1) + '0'));
078 } else {
079 int digits;
080 if (value < 1000) {
081 digits = 3;
082 } else if (value < 10000) {
083 digits = 4;
084 } else {
085 digits = (int)(Math.log(value) / LOG_10) + 1;
086 }
087 for (; size > digits; size--) {
088 buf.append('0');
089 }
090 buf.append(Integer.toString(value));
091 }
092 }
093
094 /**
095 * Converts an integer to a string, prepended with a variable amount of '0'
096 * pad characters, and appends it to the given buffer.
097 *
098 * <p>This method is optimized for converting small values to strings.
099 *
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 }