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 }