1 4 package gnu.text; 5 import java.io.*; 6 import gnu.mapping.ThreadLocation; 7 import gnu.lists.LList; 8 9 14 15 public class PrettyWriter extends java.io.Writer 16 { 17 protected Writer out; 18 19 public PrettyWriter(java.io.Writer out) 20 { 21 this.out = out; 22 prettyPrintingMode = 1; 23 } 24 25 public PrettyWriter(java.io.Writer out, int lineLength) 26 { 27 this.out = out; 28 this.lineLength = lineLength; 29 prettyPrintingMode = lineLength > 1 ? 1 : 0; 30 } 31 32 public PrettyWriter(java.io.Writer out, boolean prettyPrintingMode) 33 { 34 this.out = out; 35 this.prettyPrintingMode = prettyPrintingMode ? 1 : 0; 36 } 37 38 39 int lineLength = 80; 40 int miserWidth = 40; 41 42 public static ThreadLocation lineLengthLoc 43 = new ThreadLocation("line-length"); 44 public static ThreadLocation miserWidthLoc 45 = new ThreadLocation("miser-width"); 46 public static ThreadLocation indentLoc 47 = new ThreadLocation("indent"); 48 49 51 int prettyPrintingMode; 52 53 59 public void setPrettyPrintingMode (int mode) 60 { prettyPrintingMode = mode; } 61 62 65 public int getPrettyPrintingMode () { return prettyPrintingMode; } 66 67 68 public boolean isPrettyPrinting () { return prettyPrintingMode > 0; } 69 70 73 public void setPrettyPrinting (boolean mode) 74 { 75 prettyPrintingMode = mode ? 0 : 1; 76 } 77 78 public static int initialBufferSize = 126; 79 80 81 public char[] buffer = new char[initialBufferSize]; 82 83 84 public int bufferFillPointer; 85 86 90 int bufferOffset; 91 92 96 int bufferStartColumn; 97 98 100 int lineNumber; 101 102 108 private int indexPosn (int index) 109 { 110 return index + bufferOffset; 111 } 112 113 private int posnIndex (int posn) 114 { 115 return posn - bufferOffset; 116 } 117 118 private int posnColumn (int posn) 119 { 120 return indexColumn(posnIndex(posn)); 121 } 122 123 126 int[] blocks = new int[10 * LOGICAL_BLOCK_LENGTH]; 127 128 static final private int LOGICAL_BLOCK_LENGTH = 6; 129 static final private int BLOCK_START_COLUMN = -1; 130 static final private int BLOCK_SECTION_COLUMN = -2; 131 static final private int BLOCK_PER_LINE_PREFIX_END = -3; 132 static final private int BLOCK_PREFIX_LENGTH = -4; 133 static final private int BLOCK_SUFFIX_LENGTH = -5; 134 static final private int BLOCK_SECTION_START_LINE = -6; 135 136 int blockDepth = LOGICAL_BLOCK_LENGTH; 137 138 141 char[] prefix = new char[initialBufferSize]; 142 143 146 char[] suffix = new char[initialBufferSize]; 147 148 static final int QUEUE_INIT_ALLOC_SIZE = 300; 150 166 int[] queueInts = new int[QUEUE_INIT_ALLOC_SIZE]; 167 168 169 String [] queueStrings = new String [QUEUE_INIT_ALLOC_SIZE]; 170 171 int queueTail; 172 173 int queueSize; 174 177 int currentBlock = -1; 178 179 public int pendingBlocksCount; 180 181 185 static final int QITEM_TYPE_AND_SIZE = 0; 186 private int getQueueType(int index) { return queueInts[index] & 0xFF; } 187 private int getQueueSize(int index) { return queueInts[index] >> 16; } 188 189 static final int QITEM_POSN = 1; 190 191 static final int QITEM_BASE_SIZE = 2; 192 193 195 static final int QITEM_NOP_TYPE = 0; 196 197 201 202 static final int QITEM_SECTION_START_SIZE = QITEM_BASE_SIZE + 2; 203 static final int QITEM_SECTION_START_DEPTH = QITEM_BASE_SIZE; 204 static final int QITEM_SECTION_START_SECTION_END = QITEM_BASE_SIZE + 1; 205 206 207 static final int QITEM_NEWLINE_TYPE = 2; 208 static final int QITEM_NEWLINE_SIZE = QITEM_SECTION_START_SIZE + 1; 209 static final int QITEM_NEWLINE_KIND = QITEM_SECTION_START_SIZE; 210 public static final int NEWLINE_LINEAR = 'N'; 211 public static final int NEWLINE_LITERAL = 'L'; 212 public static final int NEWLINE_FILL = 'F'; 213 215 public static final int NEWLINE_SPACE = 'S'; 216 public static final int NEWLINE_MISER = 'M'; 217 public static final int NEWLINE_MANDATORY = 'R'; 219 static final int QITEM_INDENTATION_TYPE = 3; 220 static final int QITEM_INDENTATION_SIZE = QITEM_BASE_SIZE + 2; 221 static final int QITEM_INDENTATION_KIND = QITEM_BASE_SIZE; 222 static final char QITEM_INDENTATION_BLOCK = 'B'; 223 static final char QITEM_INDENTATION_CURRENT = 'C'; 224 static final int QITEM_INDENTATION_AMOUNT = QITEM_BASE_SIZE + 1; 225 226 227 static final int QITEM_BLOCK_START_TYPE = 4; 228 static final int QITEM_BLOCK_START_SIZE = QITEM_SECTION_START_SIZE + 3; 229 234 static final int QITEM_BLOCK_START_BLOCK_END = QITEM_SECTION_START_SIZE; 235 static final int QITEM_BLOCK_START_PREFIX = QITEM_SECTION_START_SIZE + 1; 236 static final int QITEM_BLOCK_START_SUFFIX = QITEM_SECTION_START_SIZE + 2; 237 238 static final int QITEM_BLOCK_END_TYPE = 5; 239 static final int QITEM_BLOCK_END_SIZE = QITEM_BASE_SIZE; 240 241 static final int QITEM_TAB_TYPE = 6; 242 static final int QITEM_TAB_SIZE = QITEM_BASE_SIZE + 3; 243 static final int QITEM_TAB_FLAGS = QITEM_BASE_SIZE; 244 static final int QITEM_TAB_IS_SECTION = 1; 245 static final int QITEM_TAB_IS_RELATIVE = 2; 246 static final int QITEM_TAB_COLNUM = QITEM_BASE_SIZE + 1; 247 static final int QITEM_TAB_COLINC = QITEM_BASE_SIZE + 2; 248 249 private int getSectionColumn() 250 { 251 return blocks[blockDepth+BLOCK_SECTION_COLUMN]; 252 } 253 254 private int getStartColumn() 255 { 256 return blocks[blockDepth+BLOCK_START_COLUMN]; 257 } 258 259 private int getPerLinePrefixEnd() 260 { 261 return blocks[blockDepth+BLOCK_PER_LINE_PREFIX_END]; 262 } 263 264 private int getPrefixLength() 265 { 266 return blocks[blockDepth+BLOCK_PREFIX_LENGTH]; 267 } 268 269 private int getSuffixLength() 270 { 271 return blocks[blockDepth+BLOCK_SUFFIX_LENGTH]; 272 } 273 274 private int getSectionStartLine() 275 { 276 return blocks[blockDepth+BLOCK_SECTION_START_LINE]; 277 } 278 279 boolean wordEndSeen; 280 281 282 public void writeWordEnd () 283 { 284 wordEndSeen = true; 285 } 286 287 291 public void writeWordStart () 292 { 293 if (wordEndSeen) 294 write(' '); 295 wordEndSeen = false; 296 } 297 298 public void clearWordEnd () 299 { 300 wordEndSeen = false; 301 } 302 303 public void write (int ch) 304 { 305 wordEndSeen = false; 306 if (ch == '\n' && prettyPrintingMode > 0) 308 enqueueNewline(NEWLINE_LITERAL); 309 else 310 { 311 ensureSpaceInBuffer(1); 312 int fillPointer = bufferFillPointer; 313 buffer[fillPointer] = (char) ch; 314 bufferFillPointer = 1 + fillPointer; 315 if (ch == ' ' && prettyPrintingMode > 1 && currentBlock < 0) 316 enqueueNewline(NEWLINE_SPACE); 317 } 318 } 319 320 public void write (String str) 321 { 322 write(str, 0, str.length()); 323 } 324 325 public void write (String str, int start, int count) 326 { 327 wordEndSeen = false; 328 while (count > 0) 330 { 331 int cnt = count; 332 int available = ensureSpaceInBuffer(count); 334 if (cnt > available) 335 cnt = available; 336 int fillPointer = bufferFillPointer; 337 count -= cnt; 338 while (--cnt >= 0) 339 { 340 char ch = str.charAt(start++); 341 if (ch == '\n' && prettyPrintingMode > 0) 342 { 343 bufferFillPointer = fillPointer; 344 enqueueNewline(NEWLINE_LITERAL); 345 fillPointer = bufferFillPointer; 346 } 347 else 348 { 349 buffer[fillPointer++] = (char) ch; 350 if (ch == ' ' && prettyPrintingMode > 1 && currentBlock < 0) 351 { 352 bufferFillPointer = fillPointer; 353 enqueueNewline(NEWLINE_SPACE); 354 fillPointer = bufferFillPointer; 355 } 356 } 357 } 358 bufferFillPointer = fillPointer; 359 } 360 } 361 362 public void write (char[] str) 363 { 364 write(str, 0, str.length); 365 } 366 367 public void write (char[] str, int start, int count) 368 { 369 wordEndSeen = false; 370 int end = start + count; 372 retry: 373 while (count > 0) 374 { 375 for (int i = start; i < end; i++) 377 { 378 char c; 379 if (prettyPrintingMode > 0 380 && ((c = str[i]) == '\n' 381 || (c == ' ' && currentBlock < 0))) 382 { 383 write(str, start, i - start); write(c); 385 start = i + 1; 386 count = end - start; 387 continue retry; 388 } 389 } 390 391 for (;;) 392 { 393 int available = ensureSpaceInBuffer(count); 394 int cnt = available < count ? available : count; 395 int fillPointer = bufferFillPointer; 396 int newFillPtr = fillPointer + cnt; 397 for (int i = fillPointer; i < newFillPtr; i++) 398 buffer[i] = str[start++]; 399 bufferFillPointer = newFillPtr; 400 count -= cnt; 401 if (count == 0) 402 break; 403 } 404 } 405 } 406 407 private void pushLogicalBlock(int column, 408 int perLineEnd, 409 int prefixLength, int suffixLength, 410 int sectionStartLine) 411 { 412 int newLength = blockDepth + LOGICAL_BLOCK_LENGTH; 413 if (newLength >= blocks.length) 414 { 415 int[] newBlocks = new int[2 * blocks.length]; 416 System.arraycopy(blocks, 0, newBlocks, 0, blockDepth); 417 blocks = newBlocks; 418 } 419 blockDepth = newLength; 420 blocks[blockDepth + BLOCK_START_COLUMN] = column; 421 blocks[blockDepth + BLOCK_SECTION_COLUMN] = column; 422 blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = perLineEnd; 423 blocks[blockDepth + BLOCK_PREFIX_LENGTH] = prefixLength; 424 blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = suffixLength; 425 blocks[blockDepth + BLOCK_SECTION_START_LINE] = sectionStartLine; 426 } 427 428 void reallyStartLogicalBlock(int column, String prefix, String suffix) 429 { 430 int perLineEnd = getPerLinePrefixEnd(); 431 int prefixLength = getPrefixLength(); 432 int suffixLength = getSuffixLength(); 433 pushLogicalBlock(column, perLineEnd, prefixLength, suffixLength, 434 lineNumber); 435 setIndentation(column); 436 if (prefix != null) 437 { 438 blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = column; 439 int plen = prefix.length(); 440 prefix.getChars(0, plen, this.suffix, column - plen); 441 } 442 if (suffix != null) 443 { 444 char[] totalSuffix = this.suffix; 448 int totalSuffixLen = totalSuffix.length; 449 int additional = suffix.length(); 450 int newSuffixLen = suffixLength + additional; 451 if (newSuffixLen > totalSuffixLen) 452 { 453 int newTotalSuffixLen = enoughSpace(totalSuffixLen, additional); 454 this.suffix = new char[newTotalSuffixLen]; 455 System.arraycopy(totalSuffix, totalSuffixLen - suffixLength, 456 this.suffix, newTotalSuffixLen - suffixLength, 457 suffixLength); 458 totalSuffixLen = newTotalSuffixLen; 459 } 460 suffix.getChars(0, additional, 461 totalSuffix, totalSuffixLen - newSuffixLen); 462 blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = newSuffixLen; 463 } 464 465 } 466 467 int enqueueTab (int flags, int colnum, int colinc) { 469 int addr = enqueue(QITEM_TAB_TYPE, QITEM_TAB_SIZE); 470 queueInts[addr + QITEM_TAB_FLAGS] = flags; 471 queueInts[addr + QITEM_TAB_COLNUM] = colnum; 472 queueInts[addr + QITEM_TAB_COLINC] = colinc; 473 return addr; 474 } 475 476 480 private static int enoughSpace(int current, int want) 481 { 482 int doubled = 2 * current; 483 int enough = current + ((5 * want) >> 2); 484 return doubled > enough ? doubled : enough; 485 } 486 487 public void setIndentation (int column) 488 { 489 char[] prefix = this.prefix; 490 int prefixLen = prefix.length; 491 int current = getPrefixLength(); 492 int minimum = getPerLinePrefixEnd(); 493 if (minimum > column) 494 column = minimum; 495 if (column > prefixLen) 496 { 497 prefix = new char[enoughSpace(prefixLen, column - prefixLen)]; 498 System.arraycopy(this.prefix, 0, prefix, 0, current); 499 this.prefix = prefix; 500 } 501 if (column > current) 502 { 503 for (int i = current; i < column; i++) 504 prefix[i] = ' '; 505 } 506 blocks[blockDepth + BLOCK_PREFIX_LENGTH] = column; 507 } 508 509 void reallyEndLogicalBlock () 510 { 511 int oldIndent = getPrefixLength(); 512 blockDepth -= LOGICAL_BLOCK_LENGTH; int newIndent = getPrefixLength(); 514 if (newIndent > oldIndent) 515 { 516 for (int i = oldIndent; i < newIndent; i++) 517 prefix[i] = ' '; 518 } 519 } 520 521 public int enqueue (int kind, int size) 522 { 523 int oldLength = queueInts.length; 524 int endAvail = oldLength - queueTail - queueSize; 525 if (endAvail > 0 && size > endAvail) 526 enqueue(QITEM_NOP_TYPE, endAvail); 527 if (queueSize + size > oldLength) 528 { 529 int newLength = enoughSpace(oldLength, size); 530 int[] newInts = new int[newLength]; 531 String [] newStrings = new String [newLength]; 532 int queueHead = queueTail + queueSize - oldLength; 533 if (queueHead > 0) 534 { System.arraycopy(queueInts, 0, newInts, 0, queueHead); 536 System.arraycopy(queueStrings, 0, newStrings, 0, queueHead); 537 } 538 int part1Len = oldLength - queueTail; 539 int deltaLength = newLength - oldLength; 540 System.arraycopy(queueInts, queueTail, 541 newInts, queueTail + deltaLength, 542 part1Len); 543 System.arraycopy(queueStrings, queueTail, 544 newStrings, queueTail + deltaLength, 545 part1Len); 546 queueInts = newInts; 547 queueStrings = newStrings; 548 if (currentBlock >= queueTail) 549 currentBlock += deltaLength; 550 queueTail += deltaLength; 551 } 552 int addr = queueTail + queueSize; 553 if (addr >= queueInts.length) 554 addr -= queueInts.length; 555 queueInts[addr + QITEM_TYPE_AND_SIZE] = kind | (size << 16); 556 if (size > 1) 557 queueInts[addr + QITEM_POSN] = indexPosn(bufferFillPointer); 558 queueSize += size; 560 return addr; 561 } 562 563 public void enqueueNewline (int kind) 564 { 565 wordEndSeen = false; 566 int depth = pendingBlocksCount; 567 int newline = enqueue(QITEM_NEWLINE_TYPE, QITEM_NEWLINE_SIZE); 569 queueInts[newline + QITEM_NEWLINE_KIND] = kind; 570 queueInts[newline + QITEM_SECTION_START_DEPTH] = pendingBlocksCount; 571 queueInts[newline + QITEM_SECTION_START_SECTION_END] = 0; 572 int entry = queueTail; 573 int todo = queueSize; 574 while (todo > 0) 575 { 576 if (entry == queueInts.length) 577 entry = 0; 578 if (entry == newline) 579 break; 580 int type = getQueueType(entry); 581 if ((type == QITEM_NEWLINE_TYPE 582 || type == QITEM_BLOCK_START_TYPE) 583 && queueInts[entry + QITEM_SECTION_START_SECTION_END] == 0 584 && depth <= queueInts[entry + QITEM_SECTION_START_DEPTH]) 585 { 586 int delta = newline - entry; 587 if (delta < 0) 588 delta += queueInts.length; 589 queueInts[entry + QITEM_SECTION_START_SECTION_END] = delta; 590 } 591 int size = getQueueSize(entry); 592 todo -= size; 593 entry += size; 594 } 595 maybeOutput (kind == NEWLINE_LITERAL || kind == NEWLINE_MANDATORY, false); 596 } 597 598 public final void writeBreak(int kind) 599 { 600 if (prettyPrintingMode > 0) 601 enqueueNewline(kind); 602 } 603 604 public int enqueueIndent (char kind, int amount) 605 { 606 int result = enqueue(QITEM_INDENTATION_TYPE, QITEM_INDENTATION_SIZE); 608 queueInts[result + QITEM_INDENTATION_KIND] = kind; 609 queueInts[result + QITEM_INDENTATION_AMOUNT] = amount; 610 return result; 611 } 612 613 public void addIndentation(int amount, boolean current) 614 { 615 if (prettyPrintingMode > 0) 616 enqueueIndent((current ? QITEM_INDENTATION_CURRENT 617 : QITEM_INDENTATION_BLOCK), 618 amount); 619 } 620 621 public void startLogicalBlock (String prefix, boolean perLine, String suffix) 622 { 623 if (queueSize == 0 && bufferFillPointer == 0) 626 { 627 Object llen = lineLengthLoc.get(null); 628 if (llen == null) 629 lineLength = 80; 630 else 631 lineLength = Integer.parseInt(llen.toString()); 632 633 Object mwidth = miserWidthLoc.get(null); 634 if (mwidth == null || mwidth == Boolean.FALSE 635 || mwidth == LList.Empty) 637 miserWidth = -1; 638 else 639 miserWidth = Integer.parseInt(mwidth.toString()); 640 641 Object indent = indentLoc.get(null); 642 } 644 if (prefix != null) 645 write(prefix); 646 if (prettyPrintingMode == 0) 647 return; 648 int start = enqueue (QITEM_BLOCK_START_TYPE, 649 QITEM_BLOCK_START_SIZE); 650 queueInts[start + QITEM_SECTION_START_DEPTH] = pendingBlocksCount; 651 queueStrings[start + QITEM_BLOCK_START_PREFIX] 652 = perLine ? prefix : null; 653 queueStrings[start + QITEM_BLOCK_START_SUFFIX] = suffix; 654 pendingBlocksCount++; 655 int outerBlock = currentBlock; 656 if (outerBlock < 0) 657 outerBlock = 0; 658 else 659 { 660 outerBlock -= start; 661 if (outerBlock > 0) 662 outerBlock -= queueInts.length; 663 } 664 queueInts[start + QITEM_BLOCK_START_BLOCK_END] = outerBlock; 665 queueInts[start + QITEM_SECTION_START_SECTION_END] = 0; 666 currentBlock = start; 667 } 668 669 public void endLogicalBlock () 670 { 671 int end = enqueue (QITEM_BLOCK_END_TYPE, QITEM_BLOCK_END_SIZE); 672 pendingBlocksCount--; 673 if (currentBlock < 0) 674 { 675 int suffixLength = blocks[blockDepth+BLOCK_SUFFIX_LENGTH]; 679 int suffixPreviousLength 680 = blocks[blockDepth - LOGICAL_BLOCK_LENGTH + BLOCK_SUFFIX_LENGTH]; 681 if (suffixLength > suffixPreviousLength) 682 write(this.suffix, 683 this.suffix.length - suffixLength, 684 suffixLength - suffixPreviousLength); 685 currentBlock = -1; 686 return; 687 } 688 int start = currentBlock; 689 int outerBlock = queueInts[start + QITEM_BLOCK_START_BLOCK_END]; 690 if (outerBlock == 0) 691 currentBlock = -1; 692 else 693 { 694 int qtailFromStart = queueTail - start; 695 if (qtailFromStart > 0) 696 qtailFromStart -= queueInts.length; 697 if (outerBlock < qtailFromStart) 698 { 699 currentBlock = -1; 702 } 703 else 704 { 705 outerBlock += start; 707 if (outerBlock < 0) 708 outerBlock += queueInts.length; 709 currentBlock = outerBlock; 710 } 711 } 712 String suffix = queueStrings[start + QITEM_BLOCK_START_SUFFIX]; 713 if (suffix != null) 714 write(suffix); 715 int endFromStart = end - start; 716 if (endFromStart < 0) endFromStart += queueInts.length; 718 queueInts[start + QITEM_BLOCK_START_BLOCK_END] = endFromStart; 719 } 721 722 public void endLogicalBlock (String suffix) 723 { 724 if (prettyPrintingMode > 0) 725 endLogicalBlock(); 726 else if (suffix != null) 727 write(suffix); 728 } 729 730 732 int computeTabSize (int tab, int sectionStart, int column) { 734 int flags = queueInts[tab + QITEM_TAB_FLAGS]; 735 boolean isSection = (flags & QITEM_TAB_IS_SECTION) != 0; 736 boolean isRelative = (flags & QITEM_TAB_IS_RELATIVE) != 0; 737 int origin = isSection ? sectionStart : 0; 738 int colnum = queueInts[tab + QITEM_TAB_COLNUM]; 739 int colinc = queueInts[tab + QITEM_TAB_COLINC]; 740 if (isRelative) 741 { 742 if (colinc > 1) 743 { 744 int newposn = column + colnum; 745 int rem = newposn % colinc; 746 if (rem != 0) 747 colnum += colinc = rem; 748 } 749 return colnum; 750 } 751 else if (column <= colnum + origin) 752 return column + origin - column; 753 else 754 return colinc - (column - origin) % colinc; 755 } 756 757 int indexColumn(int index) 758 { 759 int column = bufferStartColumn; 760 int sectionStart = getSectionColumn(); 761 int endPosn = indexPosn(index); 762 int op = queueTail; 763 int todo = queueSize; 764 while (todo > 0) 765 { 766 if (op >= queueInts.length) 768 op = 0; 769 int type = getQueueType(op); 770 if (type != QITEM_NOP_TYPE) 771 { 772 int posn = queueInts[op + QITEM_POSN]; 773 if (posn >= endPosn) 774 break; 775 if (type == QITEM_TAB_TYPE) 776 column += computeTabSize(op, sectionStart, 777 column + posnIndex (posn)); 778 else if (type == QITEM_NEWLINE_TYPE 779 || type == QITEM_BLOCK_START_TYPE) 780 sectionStart 781 = column + posnIndex(queueInts[op + QITEM_POSN]); 782 } 783 int size = getQueueSize(op); 784 todo -= size; 785 op += size; 786 } 787 return column + index; 788 } 789 790 void expandTabs (int through) 791 { 792 int numInsertions = 0; 793 int additional = 0; 794 int column = bufferStartColumn; 795 int sectionStart = getSectionColumn(); 796 int op = queueTail; 797 int todo = queueSize; 798 int blocksUsed = LOGICAL_BLOCK_LENGTH * pendingBlocksCount; 799 while (todo > 0) 800 { 801 if (op == queueInts.length) 802 op = 0; 803 if (op == through) 804 break; 805 int type = getQueueType(op); 806 if (type == QITEM_TAB_TYPE) 807 { 808 int index = posnIndex(queueInts[op + QITEM_POSN]); 809 int tabsize = computeTabSize (op, sectionStart, column + index); 810 if (tabsize != 0) 811 { 812 if (blocksUsed + 2 * numInsertions + 1 >= blocks.length) 814 { 815 int[] newBlocks = new int[2 * blocks.length]; 816 System.arraycopy(blocks, 0, newBlocks, 0, blocks.length); 817 blocks = newBlocks; 818 } 819 blocks[blocksUsed + 2 * numInsertions] = index; 820 blocks[blocksUsed + 2 * numInsertions + 1] = tabsize; 821 numInsertions++; 822 additional += tabsize; 823 column += tabsize; 824 } 825 } 826 else if (op == QITEM_NEWLINE_TYPE || op == QITEM_BLOCK_START_TYPE) 827 { 828 sectionStart = column + posnIndex(queueInts[op + QITEM_POSN]); 829 } 830 int size = getQueueSize(op); 831 todo -= size; 832 op += size; 833 } 834 if (numInsertions > 0) 835 { 836 int fillPtr = bufferFillPointer; 837 int newFillPtr = fillPtr + additional; 838 char[] buffer = this.buffer; 839 char[] newBuffer = buffer; 840 int length = buffer.length; 841 int end = fillPtr; 842 if (newFillPtr > length) 843 { 844 int newLength = enoughSpace (fillPtr, additional); 845 newBuffer = new char[newLength]; 846 this.buffer = newBuffer; 847 } 848 bufferFillPointer = newFillPtr; 849 bufferOffset -= additional; 850 for (int i = numInsertions; --i >= 0; ) 851 { 852 int srcpos = blocks[blocksUsed + 2 * i]; 853 int amount = blocks[blocksUsed + 2 * i + 1]; 854 int dstpos = srcpos + additional; 855 System.arraycopy(buffer, srcpos, newBuffer, dstpos, end - srcpos); 856 for (int j = dstpos - amount; j < dstpos; j++) 857 newBuffer[j] = ' '; 858 additional -= amount; 859 end = srcpos; 860 } 861 if (newBuffer != buffer) 862 System.arraycopy(buffer, 0, newBuffer, 0, end); 863 } 864 } 865 866 868 int ensureSpaceInBuffer (int want) 869 { 870 char[] buffer = this.buffer; 871 int length = buffer.length; 872 int fillPtr = bufferFillPointer; 873 int available = length - fillPtr; 874 if (available > 0) 875 return available; 876 else if (prettyPrintingMode > 0 && fillPtr > lineLength) 877 { 878 if (! maybeOutput(false, false)) 879 outputPartialLine(); 880 return ensureSpaceInBuffer(want); 881 } 882 else 883 { 884 int newLength = enoughSpace(length, want); 885 char[] newBuffer = new char[newLength]; 886 this.buffer = newBuffer; 887 for (int i = fillPtr; --i >= 0; ) 888 newBuffer[i] = buffer[i]; 889 return newLength - fillPtr; 890 } 891 } 892 893 boolean maybeOutput(boolean forceNewlines, boolean flushing) 894 { 895 boolean outputAnything = false; 896 loop: 898 while (queueSize > 0) 899 { 900 if (queueTail >= queueInts.length) 901 queueTail = 0; 902 int next = queueTail; 903 int type = getQueueType(next); 904 switch (type) 905 { 906 case QITEM_NEWLINE_TYPE: 907 boolean cond; 908 int fits = -1; 909 switch (queueInts[next+QITEM_NEWLINE_KIND]) 910 { 911 default: cond = true; 913 break; 914 case NEWLINE_MISER: 915 cond = isMisering(); 916 break; 917 case NEWLINE_FILL: 918 if (isMisering() 919 || (lineNumber > getSectionStartLine())) 920 { 921 cond = true; 922 break; 923 } 924 case NEWLINE_SPACE: 926 int end = queueInts[next+QITEM_SECTION_START_SECTION_END]; 927 if (end == 0) 928 end = -1; 929 else 930 { end = next + end; 932 if (end >= queueInts.length) 933 end -= queueInts.length; 934 } 935 fits = fitsOnLine(end, forceNewlines); 936 if (fits > 0) 937 cond = false; 938 else if (fits < 0 || flushing) 939 cond = true; 940 else 941 break loop; 942 break; 943 } 944 if (cond) 945 { 946 outputAnything = true; 947 try 948 { 949 if (flushing && fits == 0) 950 outputPartialLine(); 951 else 952 outputLine(next); 953 } 954 catch (IOException ex) 955 { 956 throw new RuntimeException (ex.toString()); 957 } 958 } 959 break; 960 case QITEM_INDENTATION_TYPE: 961 if (! isMisering()) 962 { 963 int kind = queueInts[next+QITEM_INDENTATION_KIND]; 964 int indent = queueInts[next+QITEM_INDENTATION_AMOUNT]; 965 if (kind == QITEM_INDENTATION_BLOCK) 966 indent += getStartColumn(); 967 else 968 indent += posnColumn(queueInts[next+QITEM_POSN]); 969 setIndentation(indent); 971 } 972 break; 973 case QITEM_BLOCK_START_TYPE: 974 int start = next; 975 int end = queueInts[next + QITEM_SECTION_START_SECTION_END]; 976 end = end > 0 ? (end + next) % queueInts.length : -1; 978 fits = fitsOnLine (end, forceNewlines); 979 if (fits > 0) 981 { 982 int endr = queueInts[next + QITEM_BLOCK_START_BLOCK_END]; 985 next = (endr + next) % queueInts.length; 987 expandTabs(next); 988 queueTail = next; 989 queueSize -= endr; 990 } 992 else if (fits < 0 || flushing) 993 { 994 String prefix = queueStrings[next + QITEM_BLOCK_START_PREFIX]; 995 String suffix = queueStrings[next + QITEM_BLOCK_START_SUFFIX]; 996 reallyStartLogicalBlock (posnColumn(queueInts[next + QITEM_POSN]), 998 prefix, suffix); 999 } 1000 else break loop; 1002 if (currentBlock == start) 1003 currentBlock = -1; 1004 break; 1005 case QITEM_BLOCK_END_TYPE: 1006 reallyEndLogicalBlock(); 1008 break; 1009 case QITEM_TAB_TYPE: 1010 expandTabs(next); 1011 break; 1012 } 1013 int size = getQueueSize(queueTail); 1014 queueSize -= size; 1015 queueTail = next + size; 1017 } 1018 return outputAnything; 1019 } 1020 1021 protected int getMiserWidth () { 1023 return miserWidth; 1025 } 1026 1027 boolean isMisering() { 1029 int mwidth = getMiserWidth (); 1030 return (mwidth > 0 1031 && lineLength - getStartColumn() <= mwidth); 1032 } 1033 1034 int getMaxLines () 1035 { 1036 return -1; 1038 } 1039 1040 boolean printReadably() 1041 { 1042 return true; 1044 } 1045 1046 1047 int fitsOnLine (int sectionEnd, boolean forceNewlines) { 1049 int available = lineLength; 1050 if (! printReadably() && getMaxLines() == lineNumber) 1051 { 1052 available -= 3; available -= getSuffixLength(); 1054 } 1055 if (sectionEnd >= 0) 1056 return posnColumn(queueInts[sectionEnd + QITEM_POSN]) <= available ? 1 : -1; 1057 if (forceNewlines) 1058 return -1; 1059 if (indexColumn(bufferFillPointer) > available) 1060 return -1; 1061 return 0; } 1063 1064 public void lineAbbreviationHappened() 1065 { 1066 } 1068 1069 1072 void outputLine (int newline) throws IOException 1073 { 1074 char[] buffer = this.buffer; 1075 int kind = queueInts[newline + QITEM_NEWLINE_KIND]; 1076 boolean isLiteral = kind == NEWLINE_LITERAL; 1077 int amountToConsume = posnIndex(queueInts[newline + QITEM_POSN]); 1078 int amountToPrint; 1079 if (isLiteral) 1080 amountToPrint = amountToConsume; 1081 else 1082 { 1083 for (int i = amountToConsume; ; ) 1085 { 1086 if (--i < 0) 1087 { 1088 amountToPrint = 0; 1089 break; 1090 } 1091 if (buffer[i] != ' ') 1092 { 1093 amountToPrint = i + 1; 1094 break; 1095 } 1096 } 1097 } 1098 out.write(buffer, 0, amountToPrint); 1099 int lineNumber = this.lineNumber; 1100 lineNumber++; 1102 if (! printReadably()) 1103 { 1104 int maxLines = getMaxLines(); 1105 if (maxLines > 0 && lineNumber >= maxLines) 1106 { 1107 out.write(" .."); 1108 int suffixLength = getSuffixLength(); 1109 if (suffixLength != 0) 1110 { 1111 char[] suffix = this.suffix; 1112 int len = suffix.length; 1113 out.write(suffix, len - suffixLength, suffixLength); 1114 } 1115 lineAbbreviationHappened(); 1117 } 1118 } 1119 this.lineNumber = lineNumber; 1120 out.write('\n'); 1121 bufferStartColumn = 0; 1122 int fillPtr = bufferFillPointer; 1123 int prefixLen = isLiteral ? getPerLinePrefixEnd() : getPrefixLength(); 1124 int shift = amountToConsume - prefixLen; 1125 int newFillPtr = fillPtr - shift; 1126 char[] newBuffer = buffer; 1127 int bufferLength = buffer.length; 1128 if (newFillPtr > bufferLength) 1129 { 1130 newBuffer = new char[enoughSpace(bufferLength, 1131 newFillPtr - bufferLength)]; 1132 this.buffer = newBuffer; 1133 } 1134 System.arraycopy(buffer, amountToConsume, newBuffer, prefixLen, 1135 fillPtr - amountToConsume); 1136 System.arraycopy(prefix, 0, newBuffer, 0, prefixLen); 1137 bufferFillPointer = newFillPtr; 1138 bufferOffset += shift; 1139 if (! isLiteral) 1140 { 1141 blocks[blockDepth+BLOCK_SECTION_COLUMN] = prefixLen; 1142 blocks[blockDepth+BLOCK_SECTION_START_LINE] = lineNumber; 1143 } 1144 } 1145 1146 void outputPartialLine () 1147 { 1148 int tail = queueTail; 1150 while (queueSize > 0 && getQueueType(tail) == QITEM_NOP_TYPE) 1151 { 1152 int size = getQueueSize(tail); 1153 queueSize -= size; 1154 tail += size; 1155 if (tail == queueInts.length) 1156 tail = 0; 1157 queueTail = tail; 1158 } 1159 int fillPtr = bufferFillPointer; 1160 int count = queueSize > 0 ? posnIndex (queueInts[tail + QITEM_POSN]) 1161 : fillPtr; 1162 int newFillPtr = fillPtr - count; 1163 if (count <= 0) 1164 throw new Error ("outputPartialLine called when nothing can be output."); 1165 try 1166 { 1167 out.write(buffer, 0, count); 1168 } 1170 catch (IOException ex) 1171 { 1172 throw new RuntimeException (ex.toString()); 1173 } 1174 bufferStartColumn += count; 1175 System.arraycopy(buffer, count, buffer, 0, newFillPtr); 1176 bufferFillPointer = newFillPtr; 1177 bufferOffset += count; 1178 } 1179 1180 public void forcePrettyOutput () throws IOException 1181 { 1182 maybeOutput(false, true); 1183 if (bufferFillPointer > 0) 1184 outputPartialLine(); 1185 expandTabs(-1); 1186 bufferStartColumn = getColumnNumber(); 1187 out.write(buffer, 0, bufferFillPointer); 1188 bufferFillPointer = 0; 1189 queueSize = 0; 1190 } 1191 1192 public void flush() 1193 { 1194 if (out == null) 1195 return; 1196 try 1197 { 1198 forcePrettyOutput(); 1199 out.flush(); 1200 } 1201 catch (IOException ex) 1202 { 1203 throw new RuntimeException (ex.toString()); 1204 } 1205 } 1206 1207 public void close() throws IOException 1208 { 1209 if (out != null) 1210 { 1211 forcePrettyOutput(); 1212 out.close(); 1213 out = null; 1214 } 1215 buffer = null; 1216 } 1217 1218 1219 public int getColumnNumber () 1220 { 1221 int i = bufferFillPointer; 1222 for (;;) 1223 { 1224 if (--i < 0) 1225 return bufferStartColumn + bufferFillPointer; 1226 char ch = buffer[i]; 1227 if (ch == '\n' || ch == '\r') 1228 return bufferFillPointer - (i+1); 1229 } 1230 } 1231 1232 public void setColumnNumber (int column) 1233 { 1234 bufferStartColumn += column - getColumnNumber (); 1235 } 1236 1237 public void clearBuffer () 1238 { 1239 bufferStartColumn = 0; 1240 bufferFillPointer = 0; 1241 lineNumber = 0; 1242 bufferOffset = 0; 1243 blockDepth = LOGICAL_BLOCK_LENGTH; 1244 queueTail = 0; 1245 queueSize = 0; 1246 pendingBlocksCount = 0; 1247 } 1248 1249 1449} 1450 | Popular Tags |