1 28 29 package com.caucho.es; 30 31 import com.caucho.util.CharBuffer; 32 33 class Printf { 34 private final static int ALT = 0x01; 35 private final static int ZERO_FILL = 0x02; 36 private final static int POS_PLUS = 0x04; 37 private final static int POS_SPACE = 0x08; 38 private final static int LALIGN = 0x10; 39 private final static int BIG = 0x20; 40 private final static int NO_TRAIL_ZERO = 0x40; 41 42 private static char []digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 43 'a', 'b', 'c', 'd', 'e', 'f'}; 44 private static char []bigDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 45 'A', 'B', 'C', 'D', 'E', 'F'}; 46 47 private Printf() 48 { 49 } 50 51 public static String sprintf(Call eval, int length) 52 throws Throwable 53 { 54 if (length == 0) 55 return ""; 56 57 CharBuffer buf = new CharBuffer(); 58 59 printf(buf, eval.getArg(0).toStr(), eval, length); 60 61 return buf.toString(); 62 } 63 64 public static CharBuffer printf(CharBuffer result, ESString format, 65 Call eval, int length) 66 throws Throwable 67 { 68 int arg = 1; 69 int len = format.length(); 70 71 for (int i = 0; i < len; i++) { 72 int ch; 73 int start = i; 74 75 if ((ch = format.charAt(i)) != '%') { 76 result.append((char) ch); 77 continue; 78 } 79 80 int flags = 0; 81 loop: 82 while (++i < len) { 83 switch ((ch = format.charAt(i))) { 84 case '0': flags |= ZERO_FILL; break; 85 case '+': flags |= POS_PLUS; break; 86 case ' ': flags |= POS_SPACE; break; 87 case '#': flags |= ALT; break; 88 case '-': flags |= LALIGN; break; 89 90 default: break loop; 91 } 92 } 93 94 int width = 0; 95 for (; i < len && (ch = format.charAt(i)) >= '0' && ch <= '9'; i++) { 96 width = 10 * width + ch - '0'; 97 } 98 99 if (i >= len) { 100 fixBits(result, format, start, i); 101 break; 102 } 103 104 int prec = 0; 105 if (ch == '.') { 106 while (++i < len && (ch = format.charAt(i)) >= '0' && ch <= '9') { 107 prec = 10 * prec + ch - '0'; 108 } 109 } else 110 prec = -1; 111 112 if (i >= len) { 113 fixBits(result, format, start, i); 114 break; 115 } 116 117 switch (ch) { 118 case '%': 119 result.append((char) '%'); 120 break; 121 122 case 'd': 123 if (arg >= length) 124 throw new ESException("missing printf argument"); 125 formatInteger(result, eval.getArg(arg++).toNum(), 126 width, prec, flags, 10); 127 break; 128 129 case 'o': 130 if (arg >= length) 131 throw new ESException("missing printf argument"); 132 formatInteger(result, eval.getArg(arg++).toNum(), 133 width, prec, flags, 8); 134 break; 135 136 case 'X': 137 flags |= BIG; 138 case 'x': 139 if (arg >= length) 140 throw new ESException("missing printf argument"); 141 formatInteger(result, eval.getArg(arg++).toNum(), 142 width, prec, flags, 16); 143 break; 144 145 case 'E': 146 case 'G': 147 flags |= BIG; 148 case 'f': 149 case 'e': 150 case 'g': 151 if (arg >= length) 152 throw new ESException("missing printf argument"); 153 formatDouble(result, eval.getArg(arg++).toNum(), 154 width, prec, flags, ch); 155 break; 156 157 case 'c': 158 if (arg >= length) 159 throw new ESException("missing printf argument"); 160 formatChar(result, (int) eval.getArg(arg++).toNum(), width, flags); 161 break; 162 163 case 's': 164 if (arg >= length) 165 throw new ESException("missing printf argument"); 166 formatString(result, eval.getArg(arg++).toStr(), 167 prec, width, flags); 168 break; 169 170 default: 171 fixBits(result, format, start, i + 1); 172 break; 173 } 174 } 175 176 return result; 177 } 178 179 private static void formatDouble(CharBuffer cb, double value, 180 int prec, int flags, int type) 181 { 182 String raw = Double.toString(value); 183 int expt = 0; 184 int i = 0; 185 CharBuffer digits = new CharBuffer(); 186 int ch = raw.charAt(i); 187 boolean seenDigit = false; 188 189 for (; i < raw.length(); i++) { 191 if ((ch = raw.charAt(i)) == '.' || ch == 'e' || ch == 'E') 192 break; 193 else if (! seenDigit && ch == '0') { 194 } 195 else { 196 seenDigit = true; 197 digits.append((char) ch); 198 expt++; 199 } 200 } 201 202 if (ch == '.') 203 i++; 204 205 for (; i < raw.length(); i++) { 206 ch = raw.charAt(i); 207 208 if (! seenDigit && ch == '0') { 209 expt--; 210 } else if (ch >= '0' && ch <= '9') { 211 digits.append((char) ch); 212 seenDigit = true; 213 } 214 else { 215 int sign = 1; 216 i++; 217 if ((ch = raw.charAt(i)) == '+') { 218 i++; 219 } 220 else if (ch == '-') { 221 i++; 222 sign = -1; 223 } 224 225 int e = 0; 226 for (; i < raw.length() && (ch = raw.charAt(i)) >= '0' && ch <= '9'; 227 i++) { 228 e = 10 * e + ch - '0'; 229 } 230 231 expt += sign * e; 232 break; 233 } 234 } 235 236 if (! seenDigit) 237 expt = 1; 238 239 while (digits.length() > 0 && digits.charAt(digits.length() - 1) == '0') 240 digits.setLength(digits.length() - 1); 241 242 if (type == 'f') { 243 if (roundDigits(digits, expt + prec)) { 244 expt++; 245 } 246 247 formatFixed(cb, digits, expt, prec, flags); 248 } 249 else if (type == 'e' || type == 'E') { 250 if (roundDigits(digits, prec + 1)) 251 expt++; 252 253 formatExpt(cb, digits, expt, prec, flags); 254 } 255 else { 256 if (roundDigits(digits, prec)) 257 expt++; 258 259 if (expt < -3 || expt > prec) 260 formatExpt(cb, digits, expt, prec - 1, flags|NO_TRAIL_ZERO); 261 else 262 formatFixed(cb, digits, expt, prec - expt, flags|NO_TRAIL_ZERO); 263 } 264 } 265 266 private static void formatDouble(CharBuffer cb, double value, 267 int width, int prec, int flags, 268 int type) 269 { 270 if (prec < 0) 271 prec = 6; 272 273 int offset = cb.length(); 274 275 if ((flags & ZERO_FILL) != 0 && 276 (value < 0 || (flags & (POS_PLUS|POS_SPACE)) != 0)) { 277 offset++; 278 width--; 279 } 280 281 if (value < 0) { 282 cb.append((char) '-'); 283 value = -value; 284 } else if ((flags & POS_PLUS) != 0) { 285 cb.append((char) '+'); 286 } else if ((flags & POS_SPACE) != 0) { 287 cb.append((char) ' '); 288 } 289 290 formatDouble(cb, value, prec, flags, type); 291 292 width -= cb.length() - offset; 293 294 for (int i = 0; i < width; i++) { 295 if ((flags & LALIGN) != 0) 296 cb.append(' '); 297 else 298 cb.insert(offset, (flags & ZERO_FILL) == 0 ? ' ' : '0'); 299 } 300 } 301 302 private static boolean roundDigits(CharBuffer digits, int len) 303 { 304 if (len < 0 || digits.length() <= len) 305 return false; 306 307 int value = digits.charAt(len); 308 if (value < '5') 309 return false; 310 311 for (int i = len - 1; i >= 0; i--) { 312 int ch = digits.charAt(i); 313 314 if (ch != '9') { 315 digits.setCharAt(i, (char) (ch + 1)); 316 return false; 317 } 318 digits.setCharAt(i, '0'); 319 } 320 321 digits.insert(0, '1'); 322 323 return true; 324 } 325 326 private static void formatFixed(CharBuffer cb, CharBuffer digits, 327 int expt, int prec, int flags) 328 { 329 int i = 0; 330 int origExpt = expt; 331 332 for (; expt > 0; expt--) { 333 if (i < digits.length()) 334 cb.append((char) digits.charAt(i++)); 335 else 336 cb.append('0'); 337 } 338 339 if (origExpt <= 0) cb.append('0'); 341 342 if (prec > 0 || (flags & ALT) != 0) 343 cb.append('.'); 344 345 for (; expt < 0 && prec > 0; expt++) { 346 cb.append('0'); 347 prec--; 348 } 349 350 for (; prec > 0 && i < digits.length(); i++) { 351 cb.append(digits.charAt(i)); 352 prec--; 353 } 354 355 for (; prec > 0 && (flags & (NO_TRAIL_ZERO|ALT)) != NO_TRAIL_ZERO; prec--) 356 cb.append('0'); 357 } 358 359 private static void formatExpt(CharBuffer cb, CharBuffer digits, 360 int expt, int prec, int flags) 361 { 362 if (digits.length() == 0) 363 cb.append('0'); 364 else 365 cb.append((char) digits.charAt(0)); 366 367 if (prec > 0 || (flags & ALT) != 0) 368 cb.append('.'); 369 370 for (int i = 1; i < digits.length(); i++) { 371 if (prec > 0) 372 cb.append((char) digits.charAt(i)); 373 prec--; 374 } 375 376 for (; prec > 0 && (flags & (NO_TRAIL_ZERO|ALT)) != NO_TRAIL_ZERO; prec--) 377 cb.append('0'); 378 379 if ((flags & BIG) != 0) 380 cb.append('E'); 381 else 382 cb.append('e'); 383 384 formatInteger(cb, expt - 1, 0, 2, POS_PLUS, 10); 385 } 386 387 private static void formatInteger(CharBuffer cb, double dvalue, 388 int width, int prec, int flags, int radix) 389 { 390 boolean isBig = (flags & BIG) != 0; 391 int begin = cb.length(); 392 393 long value; 394 if (dvalue > 0) 395 value = (long) (dvalue + 0.5); 396 else 397 value = (long) (dvalue - 0.5); 398 399 if (value < 0 && radix == 10) { 400 cb.append((char) '-'); 401 value = -value; 402 } else if (value >= 0 && radix == 10 && (flags & POS_PLUS) != 0) 403 cb.append((char) '+'); 404 else if (value >= 0 && radix == 10 && (flags & POS_SPACE) != 0) 405 cb.append((char) ' '); 406 else if (value < 0) 407 value &= 0xffffffffL; 408 else if (radix == 8 && (flags & ALT) != 0 && value != 0) 409 cb.append('0'); 410 else if (radix == 16 && (flags & ALT) != 0) 411 cb.append((flags & BIG) == 0 ? "0x" : "0X"); 412 413 if ((flags & ZERO_FILL) != 0) { 414 width -= cb.length() - begin; 415 begin = cb.length(); 416 } 417 418 int offset = cb.length(); 419 int len = 0; 420 while (value != 0) { 421 len++; 422 cb.insert(offset, (isBig ? bigDigits : digits)[(int) (value % radix)]); 423 value /= radix; 424 } 425 426 for (int i = 0; i < prec - len; i++) 427 cb.insert(offset, '0'); 428 if (len == 0 && prec == 0) 429 cb.insert(offset, '0'); 430 431 width -= cb.length() - begin; 432 for (; width > 0; width--) { 433 if ((flags & LALIGN) != 0) 434 cb.append(' '); 435 else if ((flags & ZERO_FILL) != 0 && prec < 0) 436 cb.insert(begin, '0'); 437 else 438 cb.insert(begin, ' '); 439 } 440 441 if (cb.length() == begin) 442 cb.append('0'); 443 } 444 445 private static void formatChar(CharBuffer cb, int ch, int width, int flags) 446 { 447 int offset = cb.length(); 448 449 cb.append((char) ch); 450 451 if ((flags & LALIGN) == 0) { 452 for (int i = 0; i < width - 1; i++) 453 cb.insert(offset, (char) ' '); 454 } else { 455 for (int i = 0; i < width - 1; i++) 456 cb.append((char) ' '); 457 } 458 } 459 460 private static void formatString(CharBuffer cb, ESString string, 461 int prec, int width, int flags) 462 { 463 int offset = cb.length(); 464 465 if (prec < 0) 466 prec = Integer.MAX_VALUE; 467 468 for (int i = 0; i < string.length() && i < prec; i++) { 469 width--; 470 cb.append(string.charAt(i)); 471 } 472 473 if ((flags & LALIGN) == 0) { 474 for (int i = 0; i < width; i++) 475 cb.insert(offset, (char) ' '); 476 } else { 477 for (int i = 0; i < width; i++) 478 cb.append((char) ' '); 479 } 480 } 481 482 private static void fixBits(CharBuffer cb, ESString format, int s, int i) 483 { 484 for (; s < i; s++) 485 cb.append((char) format.charAt(s)); 486 } 487 } 488 | Popular Tags |