1 11 package org.eclipse.jdt.internal.formatter; 12 13 import java.util.Arrays ; 14 15 import org.eclipse.jdt.core.compiler.CharOperation; 16 import org.eclipse.jdt.core.compiler.InvalidInputException; 17 import org.eclipse.jdt.internal.compiler.ASTVisitor; 18 import org.eclipse.jdt.internal.compiler.ast.Annotation; 19 import org.eclipse.jdt.internal.compiler.lookup.BlockScope; 20 import org.eclipse.jdt.internal.compiler.parser.Scanner; 21 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; 22 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; 23 import org.eclipse.jdt.internal.compiler.util.Util; 24 import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil; 25 import org.eclipse.jdt.internal.core.util.RecordedParsingInformation; 26 import org.eclipse.jdt.internal.formatter.align.Alignment; 27 import org.eclipse.jdt.internal.formatter.align.AlignmentException; 28 import org.eclipse.text.edits.MultiTextEdit; 29 import org.eclipse.text.edits.ReplaceEdit; 30 import org.eclipse.text.edits.TextEdit; 31 32 36 public class Scribe { 37 private static final int INITIAL_SIZE = 100; 38 39 private boolean checkLineWrapping; 40 41 public int column; 42 private int[][] commentPositions; 43 44 public Alignment currentAlignment; 46 public int currentToken; 47 48 private OptimizedReplaceEdit[] edits; 50 public int editsIndex; 51 52 public CodeFormatterVisitor formatter; 53 public int indentationLevel; 54 public int lastNumberOfNewLines; 55 public int line; 56 57 private int[] lineEnds; 58 private String lineSeparator; 59 public Alignment memberAlignment; 60 public boolean needSpace = false; 61 62 public int nlsTagCounter; 63 public int pageWidth; 64 public boolean pendingSpace = false; 65 66 public Scanner scanner; 67 public int scannerEndPosition; 68 public int tabLength; 69 public int indentationSize; 70 private int textRegionEnd; 71 private int textRegionStart; 72 public int tabChar; 73 public int numberOfIndentations; 74 private boolean useTabsOnlyForLeadingIndents; 75 76 77 private final boolean indentEmptyLines; 78 79 private final boolean formatJavadocComment; 80 private final boolean formatBlockComment; 81 82 Scribe(CodeFormatterVisitor formatter, long sourceLevel, int offset, int length, CodeSnippetParsingUtil codeSnippetParsingUtil) { 83 this.scanner = new Scanner(true, true, false, sourceLevel, null, null, true); 84 this.formatter = formatter; 85 this.pageWidth = formatter.preferences.page_width; 86 this.tabLength = formatter.preferences.tab_size; 87 this.indentationLevel= 0; this.numberOfIndentations = 0; 89 this.useTabsOnlyForLeadingIndents = formatter.preferences.use_tabs_only_for_leading_indentations; 90 this.indentEmptyLines = formatter.preferences.indent_empty_lines; 91 this.tabChar = formatter.preferences.tab_char; 92 if (this.tabChar == DefaultCodeFormatterOptions.MIXED) { 93 this.indentationSize = formatter.preferences.indentation_size; 94 } else { 95 this.indentationSize = this.tabLength; 96 } 97 this.lineSeparator = formatter.preferences.line_separator; 98 this.indentationLevel = formatter.preferences.initial_indentation_level * this.indentationSize; 99 this.textRegionStart = offset; 100 this.textRegionEnd = offset + length - 1; 101 if (codeSnippetParsingUtil != null) { 102 final RecordedParsingInformation information = codeSnippetParsingUtil.recordedParsingInformation; 103 if (information != null) { 104 this.lineEnds = information.lineEnds; 105 this.commentPositions = information.commentPositions; 106 } 107 } 108 this.formatBlockComment = formatter.preferences.comment_format_block_comment; 109 this.formatJavadocComment = formatter.preferences.comment_format_javadoc_comment; 110 reset(); 111 } 112 113 private final void addDeleteEdit(int start, int end) { 114 if (this.edits.length == this.editsIndex) { 115 resize(); 117 } 118 addOptimizedReplaceEdit(start, end - start + 1, Util.EMPTY_STRING); 119 } 120 121 public final void addInsertEdit(int insertPosition, String insertedString) { 122 if (this.edits.length == this.editsIndex) { 123 resize(); 125 } 126 addOptimizedReplaceEdit(insertPosition, 0, insertedString); 127 } 128 129 private final void addOptimizedReplaceEdit(int offset, int length, String replacement) { 130 if (this.editsIndex > 0) { 131 final OptimizedReplaceEdit previous = this.edits[this.editsIndex-1]; 133 final int previousOffset = previous.offset; 134 final int previousLength = previous.length; 135 final int endOffsetOfPreviousEdit = previousOffset + previousLength; 136 final int replacementLength = replacement.length(); 137 final String previousReplacement = previous.replacement; 138 final int previousReplacementLength = previousReplacement.length(); 139 if (previousOffset == offset && previousLength == length && (replacementLength == 0 || previousReplacementLength == 0)) { 140 if (this.currentAlignment != null) { 141 final Location location = this.currentAlignment.location; 142 if (location.editsIndex == this.editsIndex) { 143 location.editsIndex--; 144 location.textEdit = previous; 145 } 146 } 147 this.editsIndex--; 148 return; 149 } 150 if (endOffsetOfPreviousEdit == offset) { 151 if (length != 0) { 152 if (replacementLength != 0) { 153 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement + replacement); 154 } else if (previousLength + length == previousReplacementLength) { 155 boolean canBeRemoved = true; 157 loop: for (int i = previousOffset; i < previousOffset + previousReplacementLength; i++) { 158 if (scanner.source[i] != previousReplacement.charAt(i - previousOffset)) { 159 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousReplacementLength, previousReplacement); 160 canBeRemoved = false; 161 break loop; 162 } 163 } 164 if (canBeRemoved) { 165 if (this.currentAlignment != null) { 166 final Location location = this.currentAlignment.location; 167 if (location.editsIndex == this.editsIndex) { 168 location.editsIndex--; 169 location.textEdit = previous; 170 } 171 } 172 this.editsIndex--; 173 } 174 } else { 175 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement); 176 } 177 } else { 178 if (replacementLength != 0) { 179 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength, previousReplacement + replacement); 180 } 181 } 182 } else if ((offset + length == previousOffset) && (previousLength + length == replacementLength + previousReplacementLength)) { 183 boolean canBeRemoved = true; 185 String totalReplacement = replacement + previousReplacement; 186 loop: for (int i = 0; i < previousLength + length; i++) { 187 if (scanner.source[i + offset] != totalReplacement.charAt(i)) { 188 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(offset, previousLength + length, totalReplacement); 189 canBeRemoved = false; 190 break loop; 191 } 192 } 193 if (canBeRemoved) { 194 if (this.currentAlignment != null) { 195 final Location location = this.currentAlignment.location; 196 if (location.editsIndex == this.editsIndex) { 197 location.editsIndex--; 198 location.textEdit = previous; 199 } 200 } 201 this.editsIndex--; 202 } 203 } else { 204 this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement); 205 } 206 } else { 207 this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement); 208 } 209 } 210 211 public final void addReplaceEdit(int start, int end, String replacement) { 212 if (this.edits.length == this.editsIndex) { 213 resize(); 215 } 216 addOptimizedReplaceEdit(start, end - start + 1, replacement); 217 } 218 219 public void alignFragment(Alignment alignment, int fragmentIndex){ 220 alignment.fragmentIndex = fragmentIndex; 221 alignment.checkColumn(); 222 alignment.performFragmentEffect(); 223 } 224 225 public void checkNLSTag(int sourceStart) { 226 if (hasNLSTag(sourceStart)) { 227 this.nlsTagCounter++; 228 } 229 } 230 public void consumeNextToken() { 231 printComment(); 232 try { 233 this.currentToken = this.scanner.getNextToken(); 234 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 235 } catch (InvalidInputException e) { 236 throw new AbortFormatting(e); 237 } 238 } 239 public Alignment createAlignment(String name, int mode, int count, int sourceRestart){ 240 return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart); 241 } 242 243 public Alignment createAlignment(String name, int mode, int count, int sourceRestart, boolean adjust){ 244 return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, adjust); 245 } 246 247 public Alignment createAlignment(String name, int mode, int tieBreakRule, int count, int sourceRestart){ 248 return createAlignment(name, mode, tieBreakRule, count, sourceRestart, this.formatter.preferences.continuation_indentation, false); 249 } 250 251 public Alignment createAlignment(String name, int mode, int count, int sourceRestart, int continuationIndent, boolean adjust){ 252 return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, continuationIndent, adjust); 253 } 254 255 public Alignment createAlignment(String name, int mode, int tieBreakRule, int count, int sourceRestart, int continuationIndent, boolean adjust){ 256 Alignment alignment = new Alignment(name, mode, tieBreakRule, this, count, sourceRestart, continuationIndent); 257 if (adjust && this.memberAlignment != null) { 259 Alignment current = this.memberAlignment; 260 while (current.enclosing != null) { 261 current = current.enclosing; 262 } 263 if ((current.mode & Alignment.M_MULTICOLUMN) != 0) { 264 final int indentSize = this.indentationSize; 265 switch(current.chunkKind) { 266 case Alignment.CHUNK_METHOD : 267 case Alignment.CHUNK_TYPE : 268 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { 269 alignment.breakIndentationLevel = this.indentationLevel + indentSize; 270 } else { 271 alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize; 272 } 273 alignment.update(); 274 break; 275 case Alignment.CHUNK_FIELD : 276 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { 277 alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize; 278 } else { 279 alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize; 280 } 281 alignment.update(); 282 break; 283 } 284 } else { 285 switch(current.mode & Alignment.SPLIT_MASK) { 286 case Alignment.M_COMPACT_SPLIT : 287 case Alignment.M_COMPACT_FIRST_BREAK_SPLIT : 288 case Alignment.M_NEXT_PER_LINE_SPLIT : 289 case Alignment.M_NEXT_SHIFTED_SPLIT : 290 case Alignment.M_ONE_PER_LINE_SPLIT : 291 final int indentSize = this.indentationSize; 292 switch(current.chunkKind) { 293 case Alignment.CHUNK_METHOD : 294 case Alignment.CHUNK_TYPE : 295 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { 296 alignment.breakIndentationLevel = this.indentationLevel + indentSize; 297 } else { 298 alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize; 299 } 300 alignment.update(); 301 break; 302 case Alignment.CHUNK_FIELD : 303 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { 304 alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize; 305 } else { 306 alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize; 307 } 308 alignment.update(); 309 break; 310 } 311 break; 312 } 313 } 314 } 315 return alignment; 316 } 317 318 public Alignment createMemberAlignment(String name, int mode, int count, int sourceRestart) { 319 Alignment mAlignment = createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart); 320 mAlignment.breakIndentationLevel = this.indentationLevel; 321 return mAlignment; 322 } 323 324 public void enterAlignment(Alignment alignment){ 325 alignment.enclosing = this.currentAlignment; 326 alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart; 327 this.currentAlignment = alignment; 328 } 329 330 public void enterMemberAlignment(Alignment alignment) { 331 alignment.enclosing = this.memberAlignment; 332 alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart; 333 this.memberAlignment = alignment; 334 } 335 336 public void exitAlignment(Alignment alignment, boolean discardAlignment){ 337 Alignment current = this.currentAlignment; 338 while (current != null){ 339 if (current == alignment) break; 340 current = current.enclosing; 341 } 342 if (current == null) { 343 throw new AbortFormatting("could not find matching alignment: "+alignment); } 345 this.indentationLevel = alignment.location.outputIndentationLevel; 346 this.numberOfIndentations = alignment.location.numberOfIndentations; 347 this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart; 348 if (discardAlignment){ 349 this.currentAlignment = alignment.enclosing; 350 } 351 } 352 353 public void exitMemberAlignment(Alignment alignment){ 354 Alignment current = this.memberAlignment; 355 while (current != null){ 356 if (current == alignment) break; 357 current = current.enclosing; 358 } 359 if (current == null) { 360 throw new AbortFormatting("could not find matching alignment: "+alignment); } 362 this.indentationLevel = current.location.outputIndentationLevel; 363 this.numberOfIndentations = current.location.numberOfIndentations; 364 this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart; 365 this.memberAlignment = current.enclosing; 366 } 367 368 public Alignment getAlignment(String name){ 369 if (this.currentAlignment != null) { 370 return this.currentAlignment.getAlignment(name); 371 } 372 return null; 373 } 374 375 379 public int getColumnIndentationLevel() { 380 return this.column - 1; 381 } 382 383 public final int getCommentIndex(int position) { 384 if (this.commentPositions == null) 385 return -1; 386 int length = this.commentPositions.length; 387 if (length == 0) { 388 return -1; 389 } 390 int g = 0, d = length - 1; 391 int m = 0; 392 while (g <= d) { 393 m = g + (d - g) / 2; 394 int bound = this.commentPositions[m][1]; 395 if (bound < 0) { 396 bound = -bound; 397 } 398 if (bound < position) { 399 g = m + 1; 400 } else if (bound > position) { 401 d = m - 1; 402 } else { 403 return m; 404 } 405 } 406 return -(g + 1); 407 } 408 409 private int getCurrentCommentOffset(int start) { 410 int linePtr = -Arrays.binarySearch(this.lineEnds, start); 411 int offset = 0; 412 int beginningOfLine = this.getLineEnd(linePtr - 1); 413 if (beginningOfLine == -1) { 414 beginningOfLine = 0; 415 } 416 int currentStartPosition = start; 417 char[] source = scanner.source; 418 419 while (beginningOfLine > currentStartPosition) { 421 if (linePtr > 0) { 422 beginningOfLine = this.getLineEnd(--linePtr); 423 } else { 424 beginningOfLine = 0; 425 break; 426 } 427 } 428 for (int i = currentStartPosition - 1; i >= beginningOfLine ; i--) { 429 char currentCharacter = source[i]; 430 switch (currentCharacter) { 431 case '\t' : 432 offset += this.tabLength; 433 break; 434 case ' ' : 435 offset++; 436 break; 437 case '\r' : 438 case '\n' : 439 break; 440 default: 441 return offset; 442 } 443 } 444 return offset; 445 } 446 447 public String getEmptyLines(int linesNumber) { 448 if (this.nlsTagCounter > 0) { 449 return Util.EMPTY_STRING; 450 } 451 StringBuffer buffer = new StringBuffer (); 452 if (lastNumberOfNewLines == 0) { 453 linesNumber++; for (int i = 0; i < linesNumber; i++) { 455 if (indentEmptyLines) printIndentationIfNecessary(buffer); 456 buffer.append(this.lineSeparator); 457 } 458 lastNumberOfNewLines += linesNumber; 459 line += linesNumber; 460 column = 1; 461 needSpace = false; 462 this.pendingSpace = false; 463 } else if (lastNumberOfNewLines == 1) { 464 for (int i = 0; i < linesNumber; i++) { 465 if (indentEmptyLines) printIndentationIfNecessary(buffer); 466 buffer.append(this.lineSeparator); 467 } 468 lastNumberOfNewLines += linesNumber; 469 line += linesNumber; 470 column = 1; 471 needSpace = false; 472 this.pendingSpace = false; 473 } else { 474 if ((lastNumberOfNewLines - 1) >= linesNumber) { 475 return Util.EMPTY_STRING; 477 } 478 final int realNewLineNumber = linesNumber - lastNumberOfNewLines + 1; 479 for (int i = 0; i < realNewLineNumber; i++) { 480 if (indentEmptyLines) printIndentationIfNecessary(buffer); 481 buffer.append(this.lineSeparator); 482 } 483 lastNumberOfNewLines += realNewLineNumber; 484 line += realNewLineNumber; 485 column = 1; 486 needSpace = false; 487 this.pendingSpace = false; 488 } 489 return String.valueOf(buffer); 490 } 491 492 public OptimizedReplaceEdit getLastEdit() { 493 if (this.editsIndex > 0) { 494 return this.edits[this.editsIndex - 1]; 495 } 496 return null; 497 } 498 499 public final int getLineEnd(int lineNumber) { 500 if (this.lineEnds == null) 501 return -1; 502 if (lineNumber >= this.lineEnds.length + 1) 503 return this.scannerEndPosition; 504 if (lineNumber <= 0) 505 return -1; 506 return this.lineEnds[lineNumber-1]; } 508 509 Alignment getMemberAlignment() { 510 return this.memberAlignment; 511 } 512 513 public String getNewLine() { 514 if (this.nlsTagCounter > 0) { 515 return Util.EMPTY_STRING; 516 } 517 if (lastNumberOfNewLines >= 1) { 518 column = 1; return Util.EMPTY_STRING; 520 } 521 line++; 522 lastNumberOfNewLines = 1; 523 column = 1; 524 needSpace = false; 525 this.pendingSpace = false; 526 return this.lineSeparator; 527 } 528 529 533 public int getNextIndentationLevel(int someColumn) { 534 int indent = someColumn - 1; 535 if (indent == 0) 536 return this.indentationLevel; 537 if (this.tabChar == DefaultCodeFormatterOptions.TAB) { 538 if (this.useTabsOnlyForLeadingIndents) { 539 return indent; 540 } 541 int rem = indent % this.indentationSize; 542 int addition = rem == 0 ? 0 : this.indentationSize - rem; return indent + addition; 544 } else { 545 return indent; 546 } 547 } 548 549 private String getPreserveEmptyLines(int count) { 550 if (count > 0) { 551 if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { 552 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve); 553 return this.getEmptyLines(linesToPreserve); 554 } else { 555 return getNewLine(); 556 } 557 } 558 return Util.EMPTY_STRING; 559 } 560 561 public TextEdit getRootEdit() { 562 MultiTextEdit edit = null; 563 int length = this.textRegionEnd - this.textRegionStart + 1; 564 if (this.textRegionStart <= 0) { 565 if (length <= 0) { 566 edit = new MultiTextEdit(0, 0); 567 } else { 568 edit = new MultiTextEdit(0, this.textRegionEnd + 1); 569 } 570 } else { 571 edit = new MultiTextEdit(this.textRegionStart, this.textRegionEnd - this.textRegionStart + 1); 572 } 573 for (int i= 0, max = this.editsIndex; i < max; i++) { 574 OptimizedReplaceEdit currentEdit = edits[i]; 575 if (isValidEdit(currentEdit)) { 576 edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement)); 577 } 578 } 579 this.edits = null; 580 return edit; 581 } 582 583 public void handleLineTooLong() { 584 int relativeDepth = 0, outerMostDepth = -1; 587 Alignment targetAlignment = this.currentAlignment; 588 while (targetAlignment != null){ 589 if (targetAlignment.tieBreakRule == Alignment.R_OUTERMOST && targetAlignment.couldBreak()){ 590 outerMostDepth = relativeDepth; 591 } 592 targetAlignment = targetAlignment.enclosing; 593 relativeDepth++; 594 } 595 if (outerMostDepth >= 0) { 596 throw new AlignmentException(AlignmentException.LINE_TOO_LONG, outerMostDepth); 597 } 598 relativeDepth = 0; 600 targetAlignment = this.currentAlignment; 601 while (targetAlignment != null){ 602 if (targetAlignment.couldBreak()){ 603 throw new AlignmentException(AlignmentException.LINE_TOO_LONG, relativeDepth); 604 } 605 targetAlignment = targetAlignment.enclosing; 606 relativeDepth++; 607 } 608 } 610 611 614 private boolean hasNLSTag(int sourceStart) { 615 if (this.lineEnds == null) return false; 617 int index = Arrays.binarySearch(this.lineEnds, sourceStart); 618 int currentLineEnd = this.getLineEnd(-index); 619 if (currentLineEnd != -1) { 620 int commentIndex = getCommentIndex(currentLineEnd); 621 if (commentIndex < 0) { 622 commentIndex = -commentIndex - 2; 623 } 624 if (commentIndex >= 0 && commentIndex < this.commentPositions.length) { 625 int start = this.commentPositions[commentIndex][0]; 626 if (start < 0) { 627 start = -start; 628 int lineIndexForComment = Arrays.binarySearch(this.lineEnds, start); 630 if (lineIndexForComment == index) { 631 return CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, start, currentLineEnd) != -1; 632 } 633 } 634 } 635 } 636 return false; 637 } 638 639 public void indent() { 640 this.indentationLevel += this.indentationSize; 641 this.numberOfIndentations++; 642 } 643 644 647 public void initializeScanner(char[] compilationUnitSource) { 648 this.scanner.setSource(compilationUnitSource); 649 this.scannerEndPosition = compilationUnitSource.length; 650 this.scanner.resetTo(0, this.scannerEndPosition - 1); 651 this.edits = new OptimizedReplaceEdit[INITIAL_SIZE]; 652 } 653 654 private boolean isOnFirstColumn(int start) { 655 if (this.lineEnds == null) return start == 0; 656 int index = Arrays.binarySearch(this.lineEnds, start); 657 int previousLineEnd = this.getLineEnd(-index - 1); 659 return previousLineEnd != -1 && previousLineEnd == start - 1; 660 } 661 662 private boolean isValidEdit(OptimizedReplaceEdit edit) { 663 final int editLength= edit.length; 664 final int editReplacementLength= edit.replacement.length(); 665 final int editOffset= edit.offset; 666 if (editLength != 0) { 667 if (this.textRegionStart <= editOffset && (editOffset + editLength - 1) <= this.textRegionEnd) { 668 if (editReplacementLength != 0 && editLength == editReplacementLength) { 669 for (int i = editOffset, max = editOffset + editLength; i < max; i++) { 670 if (scanner.source[i] != edit.replacement.charAt(i - editOffset)) { 671 return true; 672 } 673 } 674 return false; 675 } else { 676 return true; 677 } 678 } else if (editOffset + editLength == this.textRegionStart) { 679 int i = editOffset; 680 for (int max = editOffset + editLength; i < max; i++) { 681 int replacementStringIndex = i - editOffset; 682 if (replacementStringIndex >= editReplacementLength || scanner.source[i] != edit.replacement.charAt(replacementStringIndex)) { 683 break; 684 } 685 } 686 if (i - editOffset != editReplacementLength && i != editOffset + editLength - 1) { 687 edit.offset = textRegionStart; 688 edit.length = 0; 689 edit.replacement = edit.replacement.substring(i - editOffset); 690 return true; 691 } 692 } 693 } else if (this.textRegionStart <= editOffset && editOffset <= this.textRegionEnd) { 694 return true; 695 } else if (editOffset == this.scannerEndPosition && editOffset == this.textRegionEnd + 1) { 696 return true; 697 } 698 return false; 699 } 700 701 private void preserveEmptyLines(int count, int insertPosition) { 702 if (count > 0) { 703 if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { 704 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve); 705 this.printEmptyLines(linesToPreserve, insertPosition); 706 } else { 707 printNewLine(insertPosition); 708 } 709 } 710 } 711 712 private void print(char[] s, boolean considerSpaceIfAny) { 713 if (checkLineWrapping && s.length + column > this.pageWidth) { 714 handleLineTooLong(); 715 } 716 this.lastNumberOfNewLines = 0; 717 if (this.indentationLevel != 0) { 718 printIndentationIfNecessary(); 719 } 720 if (considerSpaceIfAny) { 721 this.space(); 722 } 723 if (this.pendingSpace) { 724 this.addInsertEdit(this.scanner.getCurrentTokenStartPosition(), " "); } 726 this.pendingSpace = false; 727 this.needSpace = false; 728 column += s.length; 729 needSpace = true; 730 } 731 732 private void printBlockComment(char[] s, boolean isJavadoc) { 733 int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); 734 int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1; 735 736 this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1); 737 int currentCharacter; 738 boolean isNewLine = false; 739 int start = currentTokenStartPosition; 740 int nextCharacterStart = currentTokenStartPosition; 741 int previousStart = currentTokenStartPosition; 742 boolean onFirstColumn = isOnFirstColumn(start); 743 744 boolean indentComment = false; 745 if (this.indentationLevel != 0) { 746 if (isJavadoc 747 || !this.formatter.preferences.never_indent_block_comments_on_first_column 748 || !onFirstColumn) { 749 indentComment = true; 750 printIndentationIfNecessary(); 751 } 752 } 753 if (this.pendingSpace) { 754 this.addInsertEdit(currentTokenStartPosition, " "); } 756 this.needSpace = false; 757 this.pendingSpace = false; 758 759 int currentCommentOffset = onFirstColumn ? 0 : getCurrentCommentOffset(start); 760 boolean formatComment = (isJavadoc && formatJavadocComment) || (!isJavadoc && formatBlockComment); 761 762 while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) { 763 nextCharacterStart = this.scanner.currentPosition; 764 765 switch(currentCharacter) { 766 case '\r' : 767 start = previousStart; 768 isNewLine = true; 769 if (this.scanner.getNextChar('\n')) { 770 currentCharacter = '\n'; 771 nextCharacterStart = this.scanner.currentPosition; 772 } 773 break; 774 case '\n' : 775 start = previousStart; 776 isNewLine = true; 777 nextCharacterStart = this.scanner.currentPosition; 778 break; 779 default: 780 if (isNewLine) { 781 this.column = 1; 782 this.line++; 783 isNewLine = false; 784 785 StringBuffer buffer = new StringBuffer (); 786 if (onFirstColumn) { 787 buffer.append(this.lineSeparator); 789 if (indentComment) { 790 printIndentationIfNecessary(buffer); 791 } 792 if (formatComment) { 793 if (ScannerHelper.isWhitespace((char) currentCharacter)) { 794 int previousStartPosition = this.scanner.currentPosition; 795 while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) { 796 previousStart = nextCharacterStart; 797 previousStartPosition = this.scanner.currentPosition; 798 currentCharacter = this.scanner.getNextChar(); 799 nextCharacterStart = this.scanner.currentPosition; 800 } 801 if (currentCharacter == '\r' || currentCharacter == '\n') { 802 nextCharacterStart = previousStartPosition; 803 } 804 } 805 if (currentCharacter != '\r' && currentCharacter != '\n') { 806 buffer.append(' '); 807 } 808 } 809 } else { 810 if (ScannerHelper.isWhitespace((char) currentCharacter)) { 811 int previousStartPosition = this.scanner.currentPosition; 812 int count = 0; 813 loop: while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) { 814 if (count >= currentCommentOffset) { 815 break loop; 816 } 817 previousStart = nextCharacterStart; 818 previousStartPosition = this.scanner.currentPosition; 819 switch(currentCharacter) { 820 case '\t' : 821 count += this.tabLength; 822 break; 823 default : 824 count ++; 825 } 826 currentCharacter = this.scanner.getNextChar(); 827 nextCharacterStart = this.scanner.currentPosition; 828 } 829 if (currentCharacter == '\r' || currentCharacter == '\n') { 830 nextCharacterStart = previousStartPosition; 831 } 832 } 833 buffer.append(this.lineSeparator); 834 if (indentComment) { 835 printIndentationIfNecessary(buffer); 836 } 837 if (formatComment) { 838 int previousStartTemp = previousStart; 839 int nextCharacterStartTemp = nextCharacterStart; 840 while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) { 841 previousStart = nextCharacterStart; 842 currentCharacter = this.scanner.getNextChar(); 843 nextCharacterStart = this.scanner.currentPosition; 844 } 845 if (currentCharacter == '*') { 846 buffer.append(' '); 847 } else { 848 previousStart = previousStartTemp; 849 nextCharacterStart = nextCharacterStartTemp; 850 } 851 this.scanner.currentPosition = nextCharacterStart; 852 } 853 } 854 addReplaceEdit(start, previousStart - 1, String.valueOf(buffer)); 855 } else { 856 this.column += (nextCharacterStart - previousStart); 857 } 858 } 859 previousStart = nextCharacterStart; 860 this.scanner.currentPosition = nextCharacterStart; 861 } 862 this.lastNumberOfNewLines = 0; 863 needSpace = false; 864 this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1); 865 if (isJavadoc) { 866 printNewLine(); 867 } 868 } 869 870 public void printEndOfCompilationUnit() { 871 try { 872 int currentTokenStartPosition = this.scanner.currentPosition; 874 boolean hasComment = false; 875 boolean hasLineComment = false; 876 boolean hasWhitespace = false; 877 int count = 0; 878 while (true) { 879 this.currentToken = this.scanner.getNextToken(); 880 switch(this.currentToken) { 881 case TerminalTokens.TokenNameWHITESPACE : 882 char[] whiteSpaces = this.scanner.getCurrentTokenSource(); 883 count = 0; 884 for (int i = 0, max = whiteSpaces.length; i < max; i++) { 885 switch(whiteSpaces[i]) { 886 case '\r' : 887 if ((i + 1) < max) { 888 if (whiteSpaces[i + 1] == '\n') { 889 i++; 890 } 891 } 892 count++; 893 break; 894 case '\n' : 895 count++; 896 } 897 } 898 if (count == 0) { 899 hasWhitespace = true; 900 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 901 } else if (hasComment) { 902 if (count == 1) { 903 this.printNewLine(this.scanner.getCurrentTokenStartPosition()); 904 } else { 905 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 906 } 907 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 908 } else if (hasLineComment) { 909 this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition()); 910 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 911 } else { 912 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 913 } 914 currentTokenStartPosition = this.scanner.currentPosition; 915 break; 916 case TerminalTokens.TokenNameCOMMENT_LINE : 917 if (count >= 1) { 918 if (count > 1) { 919 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 920 } else if (count == 1) { 921 printNewLine(this.scanner.getCurrentTokenStartPosition()); 922 } 923 } else if (hasWhitespace) { 924 space(); 925 } 926 hasWhitespace = false; 927 this.printLineComment(this.scanner.getRawTokenSource()); 928 currentTokenStartPosition = this.scanner.currentPosition; 929 hasLineComment = true; 930 count = 0; 931 break; 932 case TerminalTokens.TokenNameCOMMENT_BLOCK : 933 if (count >= 1) { 934 if (count > 1) { 935 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 936 } else if (count == 1) { 937 printNewLine(this.scanner.getCurrentTokenStartPosition()); 938 } 939 } else if (hasWhitespace) { 940 space(); 941 } 942 hasWhitespace = false; 943 this.printBlockComment(this.scanner.getRawTokenSource(), false); 944 currentTokenStartPosition = this.scanner.currentPosition; 945 hasLineComment = false; 946 hasComment = true; 947 count = 0; 948 break; 949 case TerminalTokens.TokenNameCOMMENT_JAVADOC : 950 if (count >= 1) { 951 if (count > 1) { 952 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 953 } else if (count == 1) { 954 printNewLine(this.scanner.getCurrentTokenStartPosition()); 955 } 956 } else if (hasWhitespace) { 957 space(); 958 } 959 hasWhitespace = false; 960 this.printBlockComment(this.scanner.getRawTokenSource(), true); 961 currentTokenStartPosition = this.scanner.currentPosition; 962 hasLineComment = false; 963 hasComment = true; 964 count = 0; 965 break; 966 case TerminalTokens.TokenNameSEMICOLON : 967 char[] currentTokenSource = this.scanner.getRawTokenSource(); 968 this.print(currentTokenSource, this.formatter.preferences.insert_space_before_semicolon); 969 break; 970 case TerminalTokens.TokenNameEOF : 971 if (count >= 1 || this.formatter.preferences.insert_new_line_at_end_of_file_if_missing) { 972 this.printNewLine(this.scannerEndPosition); 973 } 974 return; 975 default : 976 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 978 return; 979 } 980 } 981 } catch (InvalidInputException e) { 982 throw new AbortFormatting(e); 983 } 984 } 985 986 public void printComment() { 987 try { 988 int currentTokenStartPosition = this.scanner.currentPosition; 990 boolean hasComment = false; 991 boolean hasLineComment = false; 992 boolean hasWhitespace = false; 993 int count = 0; 994 while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { 995 switch(this.currentToken) { 996 case TerminalTokens.TokenNameWHITESPACE : 997 char[] whiteSpaces = this.scanner.getCurrentTokenSource(); 998 count = 0; 999 for (int i = 0, max = whiteSpaces.length; i < max; i++) { 1000 switch(whiteSpaces[i]) { 1001 case '\r' : 1002 if ((i + 1) < max) { 1003 if (whiteSpaces[i + 1] == '\n') { 1004 i++; 1005 } 1006 } 1007 count++; 1008 break; 1009 case '\n' : 1010 count++; 1011 } 1012 } 1013 if (count == 0) { 1014 hasWhitespace = true; 1015 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1016 } else if (hasComment) { 1017 if (count == 1) { 1018 this.printNewLine(this.scanner.getCurrentTokenStartPosition()); 1019 } else { 1020 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 1021 } 1022 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1023 } else if (hasLineComment) { 1024 this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition()); 1025 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1026 } else if (count != 0 && this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { 1027 addReplaceEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition(), this.getPreserveEmptyLines(count - 1)); 1028 } else { 1029 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1030 } 1031 currentTokenStartPosition = this.scanner.currentPosition; 1032 break; 1033 case TerminalTokens.TokenNameCOMMENT_LINE : 1034 if (count >= 1) { 1035 if (count > 1) { 1036 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 1037 } else if (count == 1) { 1038 printNewLine(this.scanner.getCurrentTokenStartPosition()); 1039 } 1040 } else if (hasWhitespace) { 1041 space(); 1042 } 1043 hasWhitespace = false; 1044 this.printLineComment(this.scanner.getRawTokenSource()); 1045 currentTokenStartPosition = this.scanner.currentPosition; 1046 hasLineComment = true; 1047 count = 0; 1048 break; 1049 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1050 if (count >= 1) { 1051 if (count > 1) { 1052 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 1053 } else if (count == 1) { 1054 printNewLine(this.scanner.getCurrentTokenStartPosition()); 1055 } 1056 } else if (hasWhitespace) { 1057 space(); 1058 } 1059 hasWhitespace = false; 1060 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1061 currentTokenStartPosition = this.scanner.currentPosition; 1062 hasLineComment = false; 1063 hasComment = true; 1064 count = 0; 1065 break; 1066 case TerminalTokens.TokenNameCOMMENT_JAVADOC : 1067 if (count >= 1) { 1068 if (count > 1) { 1069 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); 1070 } else if (count == 1) { 1071 printNewLine(this.scanner.getCurrentTokenStartPosition()); 1072 } 1073 } else if (hasWhitespace) { 1074 space(); 1075 } 1076 hasWhitespace = false; 1077 this.printBlockComment(this.scanner.getRawTokenSource(), true); 1078 currentTokenStartPosition = this.scanner.currentPosition; 1079 hasLineComment = false; 1080 hasComment = true; 1081 count = 0; 1082 break; 1083 default : 1084 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1086 return; 1087 } 1088 } 1089 } catch (InvalidInputException e) { 1090 throw new AbortFormatting(e); 1091 } 1092 } 1093 1094 private void printLineComment(char[] s) { 1095 int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); 1096 int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1; 1097 if (CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, currentTokenStartPosition, currentTokenEndPosition) != -1) { 1098 this.nlsTagCounter = 0; 1099 } 1100 this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1); 1101 int currentCharacter; 1102 int start = currentTokenStartPosition; 1103 int nextCharacterStart = currentTokenStartPosition; 1104 1105 if (this.indentationLevel != 0) { 1106 if (!this.formatter.preferences.never_indent_line_comments_on_first_column 1107 || !isOnFirstColumn(start)) { 1108 printIndentationIfNecessary(); 1109 } 1110 } 1111 if (this.pendingSpace) { 1112 this.addInsertEdit(currentTokenStartPosition, " "); } 1114 this.needSpace = false; 1115 this.pendingSpace = false; 1116 int previousStart = currentTokenStartPosition; 1117 1118 loop: while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) { 1119 nextCharacterStart = this.scanner.currentPosition; 1120 1121 switch(currentCharacter) { 1122 case '\r' : 1123 start = previousStart; 1124 break loop; 1125 case '\n' : 1126 start = previousStart; 1127 break loop; 1128 } 1129 previousStart = nextCharacterStart; 1130 } 1131 if (start != currentTokenStartPosition) { 1132 addReplaceEdit(start, currentTokenEndPosition - 1, lineSeparator); 1134 this.line++; 1135 this.column = 1; 1136 this.lastNumberOfNewLines = 1; 1137 } 1138 this.needSpace = false; 1139 this.pendingSpace = false; 1140 if (this.currentAlignment != null) { 1142 if (this.memberAlignment != null) { 1143 if (this.currentAlignment.location.inputOffset > this.memberAlignment.location.inputOffset) { 1145 if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) { 1146 this.currentAlignment.performFragmentEffect(); 1147 } 1148 } else { 1149 this.indentationLevel = Math.max(this.indentationLevel, this.memberAlignment.breakIndentationLevel); 1150 } 1151 } else if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) { 1152 this.currentAlignment.performFragmentEffect(); 1153 } 1154 } 1155 this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1); 1156 } 1157 public void printEmptyLines(int linesNumber) { 1158 this.printEmptyLines(linesNumber, this.scanner.getCurrentTokenEndPosition() + 1); 1159 } 1160 1161 private void printEmptyLines(int linesNumber, int insertPosition) { 1162 final String buffer = getEmptyLines(linesNumber); 1163 if (Util.EMPTY_STRING == buffer) return; 1164 1165 addInsertEdit(insertPosition, buffer); 1166 } 1167 1168 void printIndentationIfNecessary() { 1169 StringBuffer buffer = new StringBuffer (); 1170 printIndentationIfNecessary(buffer); 1171 if (buffer.length() > 0) { 1172 addInsertEdit(this.scanner.getCurrentTokenStartPosition(), buffer.toString()); 1173 this.pendingSpace = false; 1174 } 1175 } 1176 1177 private void printIndentationIfNecessary(StringBuffer buffer) { 1178 switch(this.tabChar) { 1179 case DefaultCodeFormatterOptions.TAB : 1180 boolean useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents; 1181 int numberOfLeadingIndents = this.numberOfIndentations; 1182 int indentationsAsTab = 0; 1183 if (useTabsForLeadingIndents) { 1184 while (this.column <= this.indentationLevel) { 1185 if (indentationsAsTab < numberOfLeadingIndents) { 1186 buffer.append('\t'); 1187 indentationsAsTab++; 1188 int complement = this.tabLength - ((this.column - 1) % this.tabLength); this.column += complement; 1190 this.needSpace = false; 1191 } else { 1192 buffer.append(' '); 1193 this.column++; 1194 this.needSpace = false; 1195 } 1196 } 1197 } else { 1198 while (this.column <= this.indentationLevel) { 1199 buffer.append('\t'); 1200 int complement = this.tabLength - ((this.column - 1) % this.tabLength); this.column += complement; 1202 this.needSpace = false; 1203 } 1204 } 1205 break; 1206 case DefaultCodeFormatterOptions.SPACE : 1207 while (this.column <= this.indentationLevel) { 1208 buffer.append(' '); 1209 this.column++; 1210 this.needSpace = false; 1211 } 1212 break; 1213 case DefaultCodeFormatterOptions.MIXED : 1214 useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents; 1215 numberOfLeadingIndents = this.numberOfIndentations; 1216 indentationsAsTab = 0; 1217 if (useTabsForLeadingIndents) { 1218 final int columnForLeadingIndents = numberOfLeadingIndents * this.indentationSize; 1219 while (this.column <= this.indentationLevel) { 1220 if (this.column <= columnForLeadingIndents) { 1221 if ((this.column - 1 + this.tabLength) <= this.indentationLevel) { 1222 buffer.append('\t'); 1223 this.column += this.tabLength; 1224 } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) { 1225 for (int i = 0, max = this.indentationSize; i < max; i++) { 1227 buffer.append(' '); 1228 this.column++; 1229 } 1230 } else { 1231 buffer.append(' '); 1232 this.column++; 1233 } 1234 } else { 1235 for (int i = this.column, max = this.indentationLevel; i <= max; i++) { 1236 buffer.append(' '); 1237 this.column++; 1238 } 1239 } 1240 this.needSpace = false; 1241 } 1242 } else { 1243 while (this.column <= this.indentationLevel) { 1244 if ((this.column - 1 + this.tabLength) <= this.indentationLevel) { 1245 buffer.append('\t'); 1246 this.column += this.tabLength; 1247 } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) { 1248 for (int i = 0, max = this.indentationSize; i < max; i++) { 1250 buffer.append(' '); 1251 this.column++; 1252 } 1253 } else { 1254 buffer.append(' '); 1255 this.column++; 1256 } 1257 this.needSpace = false; 1258 } 1259 } 1260 break; 1261 } 1262 } 1263 1264 public void printModifiers(Annotation[] annotations, ASTVisitor visitor) { 1265 try { 1266 int annotationsLength = annotations != null ? annotations.length : 0; 1267 int annotationsIndex = 0; 1268 boolean isFirstModifier = true; 1269 int currentTokenStartPosition = this.scanner.currentPosition; 1270 boolean hasComment = false; 1271 boolean hasModifiers = false; 1272 while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { 1273 switch(this.currentToken) { 1274 case TerminalTokens.TokenNamepublic : 1275 case TerminalTokens.TokenNameprotected : 1276 case TerminalTokens.TokenNameprivate : 1277 case TerminalTokens.TokenNamestatic : 1278 case TerminalTokens.TokenNameabstract : 1279 case TerminalTokens.TokenNamefinal : 1280 case TerminalTokens.TokenNamenative : 1281 case TerminalTokens.TokenNamesynchronized : 1282 case TerminalTokens.TokenNametransient : 1283 case TerminalTokens.TokenNamevolatile : 1284 case TerminalTokens.TokenNamestrictfp : 1285 hasModifiers = true; 1286 this.print(this.scanner.getRawTokenSource(), !isFirstModifier); 1287 isFirstModifier = false; 1288 currentTokenStartPosition = this.scanner.currentPosition; 1289 break; 1290 case TerminalTokens.TokenNameAT : 1291 hasModifiers = true; 1292 if (!isFirstModifier) { 1293 this.space(); 1294 } 1295 this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1); 1296 if (annotationsIndex < annotationsLength) { 1297 annotations[annotationsIndex++].traverse(visitor, (BlockScope) null); 1298 if (this.formatter.preferences.insert_new_line_after_annotation) { 1299 this.printNewLine(); 1300 } 1301 } else { 1302 return; 1303 } 1304 isFirstModifier = false; 1305 currentTokenStartPosition = this.scanner.currentPosition; 1306 break; 1307 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1308 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1309 currentTokenStartPosition = this.scanner.currentPosition; 1310 hasComment = true; 1311 break; 1312 case TerminalTokens.TokenNameCOMMENT_JAVADOC : 1313 this.printBlockComment(this.scanner.getRawTokenSource(), true); 1314 currentTokenStartPosition = this.scanner.currentPosition; 1315 hasComment = true; 1316 break; 1317 case TerminalTokens.TokenNameCOMMENT_LINE : 1318 this.printLineComment(this.scanner.getRawTokenSource()); 1319 currentTokenStartPosition = this.scanner.currentPosition; 1320 break; 1321 case TerminalTokens.TokenNameWHITESPACE : 1322 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1323 int count = 0; 1324 char[] whiteSpaces = this.scanner.getCurrentTokenSource(); 1325 for (int i = 0, max = whiteSpaces.length; i < max; i++) { 1326 switch(whiteSpaces[i]) { 1327 case '\r' : 1328 if ((i + 1) < max) { 1329 if (whiteSpaces[i + 1] == '\n') { 1330 i++; 1331 } 1332 } 1333 count++; 1334 break; 1335 case '\n' : 1336 count++; 1337 } 1338 } 1339 if (count >= 1 && hasComment) { 1340 printNewLine(); 1341 } 1342 currentTokenStartPosition = this.scanner.currentPosition; 1343 hasComment = false; 1344 break; 1345 default: 1346 if (hasModifiers) { 1347 this.space(); 1348 } 1349 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1351 return; 1352 } 1353 } 1354 } catch (InvalidInputException e) { 1355 throw new AbortFormatting(e); 1356 } 1357 } 1358 1359 public void printNewLine() { 1360 if (this.nlsTagCounter > 0) { 1361 return; 1362 } 1363 if (lastNumberOfNewLines >= 1) { 1364 column = 1; return; 1366 } 1367 addInsertEdit(this.scanner.getCurrentTokenEndPosition() + 1, this.lineSeparator); 1368 line++; 1369 lastNumberOfNewLines = 1; 1370 column = 1; 1371 needSpace = false; 1372 this.pendingSpace = false; 1373 } 1374 1375 public void printNewLine(int insertPosition) { 1376 if (this.nlsTagCounter > 0) { 1377 return; 1378 } 1379 if (lastNumberOfNewLines >= 1) { 1380 column = 1; return; 1382 } 1383 addInsertEdit(insertPosition, this.lineSeparator); 1384 line++; 1385 lastNumberOfNewLines = 1; 1386 column = 1; 1387 needSpace = false; 1388 this.pendingSpace = false; 1389 } 1390 1391 public void printNextToken(int expectedTokenType){ 1392 printNextToken(expectedTokenType, false); 1393 } 1394 1395 public void printNextToken(int expectedTokenType, boolean considerSpaceIfAny){ 1396 printComment(); 1397 try { 1398 this.currentToken = this.scanner.getNextToken(); 1399 char[] currentTokenSource = this.scanner.getRawTokenSource(); 1400 if (expectedTokenType != this.currentToken) { 1401 throw new AbortFormatting("unexpected token type, expecting:"+expectedTokenType+", actual:"+this.currentToken); } 1403 this.print(currentTokenSource, considerSpaceIfAny); 1404 } catch (InvalidInputException e) { 1405 throw new AbortFormatting(e); 1406 } 1407 } 1408 1409 public void printNextToken(int[] expectedTokenTypes) { 1410 printNextToken(expectedTokenTypes, false); 1411 } 1412 1413 public void printNextToken(int[] expectedTokenTypes, boolean considerSpaceIfAny){ 1414 printComment(); 1415 try { 1416 this.currentToken = this.scanner.getNextToken(); 1417 char[] currentTokenSource = this.scanner.getRawTokenSource(); 1418 if (Arrays.binarySearch(expectedTokenTypes, this.currentToken) < 0) { 1419 StringBuffer expectations = new StringBuffer (5); 1420 for (int i = 0; i < expectedTokenTypes.length; i++){ 1421 if (i > 0) { 1422 expectations.append(','); 1423 } 1424 expectations.append(expectedTokenTypes[i]); 1425 } 1426 throw new AbortFormatting("unexpected token type, expecting:["+expectations.toString()+"], actual:"+this.currentToken); } 1428 this.print(currentTokenSource, considerSpaceIfAny); 1429 } catch (InvalidInputException e) { 1430 throw new AbortFormatting(e); 1431 } 1432 } 1433 1434 public void printArrayQualifiedReference(int numberOfTokens, int sourceEnd) { 1435 int currentTokenStartPosition = this.scanner.currentPosition; 1436 int numberOfIdentifiers = 0; 1437 try { 1438 do { 1439 this.printComment(); 1440 switch(this.currentToken = this.scanner.getNextToken()) { 1441 case TerminalTokens.TokenNameEOF : 1442 return; 1443 case TerminalTokens.TokenNameWHITESPACE : 1444 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1445 currentTokenStartPosition = this.scanner.currentPosition; 1446 break; 1447 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1448 case TerminalTokens.TokenNameCOMMENT_JAVADOC : 1449 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1450 currentTokenStartPosition = this.scanner.currentPosition; 1451 break; 1452 case TerminalTokens.TokenNameCOMMENT_LINE : 1453 this.printLineComment(this.scanner.getRawTokenSource()); 1454 currentTokenStartPosition = this.scanner.currentPosition; 1455 break; 1456 case TerminalTokens.TokenNameIdentifier : 1457 this.print(this.scanner.getRawTokenSource(), false); 1458 currentTokenStartPosition = this.scanner.currentPosition; 1459 if (++ numberOfIdentifiers == numberOfTokens) { 1460 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1461 return; 1462 } 1463 break; 1464 case TerminalTokens.TokenNameDOT : 1465 this.print(this.scanner.getRawTokenSource(), false); 1466 currentTokenStartPosition = this.scanner.currentPosition; 1467 break; 1468 default: 1469 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1470 return; 1471 } 1472 } while (this.scanner.currentPosition <= sourceEnd); 1473 } catch(InvalidInputException e) { 1474 throw new AbortFormatting(e); 1475 } 1476 } 1477 1478 public void printQualifiedReference(int sourceEnd) { 1479 int currentTokenStartPosition = this.scanner.currentPosition; 1480 try { 1481 do { 1482 this.printComment(); 1483 switch(this.currentToken = this.scanner.getNextToken()) { 1484 case TerminalTokens.TokenNameEOF : 1485 return; 1486 case TerminalTokens.TokenNameWHITESPACE : 1487 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1488 currentTokenStartPosition = this.scanner.currentPosition; 1489 break; 1490 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1491 case TerminalTokens.TokenNameCOMMENT_JAVADOC : 1492 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1493 currentTokenStartPosition = this.scanner.currentPosition; 1494 break; 1495 case TerminalTokens.TokenNameCOMMENT_LINE : 1496 this.printLineComment(this.scanner.getRawTokenSource()); 1497 currentTokenStartPosition = this.scanner.currentPosition; 1498 break; 1499 case TerminalTokens.TokenNameIdentifier : 1500 case TerminalTokens.TokenNameDOT : 1501 this.print(this.scanner.getRawTokenSource(), false); 1502 currentTokenStartPosition = this.scanner.currentPosition; 1503 break; 1504 default: 1505 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1506 return; 1507 } 1508 } while (this.scanner.currentPosition <= sourceEnd); 1509 } catch(InvalidInputException e) { 1510 throw new AbortFormatting(e); 1511 } 1512 } 1513 1514 private void printRule(StringBuffer stringBuffer) { 1515 for (int i = 0; i < this.pageWidth; i++){ 1516 if ((i % this.tabLength) == 0) { 1517 stringBuffer.append('+'); 1518 } else { 1519 stringBuffer.append('-'); 1520 } 1521 } 1522 stringBuffer.append(this.lineSeparator); 1523 1524 for (int i = 0; i < (pageWidth / tabLength); i++) { 1525 stringBuffer.append(i); 1526 stringBuffer.append('\t'); 1527 } 1528 } 1529 1530 public void printTrailingComment(int numberOfNewLinesToInsert) { 1531 try { 1532 int currentTokenStartPosition = this.scanner.currentPosition; 1534 boolean hasWhitespaces = false; 1535 boolean hasLineComment = false; 1536 while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { 1537 switch(this.currentToken) { 1538 case TerminalTokens.TokenNameWHITESPACE : 1539 int count = 0; 1540 char[] whiteSpaces = this.scanner.getCurrentTokenSource(); 1541 for (int i = 0, max = whiteSpaces.length; i < max; i++) { 1542 switch(whiteSpaces[i]) { 1543 case '\r' : 1544 if ((i + 1) < max) { 1545 if (whiteSpaces[i + 1] == '\n') { 1546 i++; 1547 } 1548 } 1549 count++; 1550 break; 1551 case '\n' : 1552 count++; 1553 } 1554 } 1555 if (hasLineComment) { 1556 if (count >= 1) { 1557 currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); 1558 this.preserveEmptyLines(numberOfNewLinesToInsert, currentTokenStartPosition); 1559 addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition()); 1560 this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1); 1561 return; 1562 } else { 1563 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1564 return; 1565 } 1566 } else if (count > 1) { 1567 this.printEmptyLines(numberOfNewLinesToInsert, this.scanner.getCurrentTokenStartPosition()); 1568 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1569 return; 1570 } else { 1571 hasWhitespaces = true; 1572 currentTokenStartPosition = this.scanner.currentPosition; 1573 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1574 } 1575 break; 1576 case TerminalTokens.TokenNameCOMMENT_LINE : 1577 if (hasWhitespaces) { 1578 space(); 1579 } 1580 this.printLineComment(this.scanner.getRawTokenSource()); 1581 currentTokenStartPosition = this.scanner.currentPosition; 1582 hasLineComment = true; 1583 break; 1584 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1585 if (hasWhitespaces) { 1586 space(); 1587 } 1588 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1589 currentTokenStartPosition = this.scanner.currentPosition; 1590 break; 1591 default : 1592 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1594 return; 1595 } 1596 } 1597 } catch (InvalidInputException e) { 1598 throw new AbortFormatting(e); 1599 } 1600 } 1601 public void printTrailingComment() { 1602 try { 1603 int currentTokenStartPosition = this.scanner.currentPosition; 1605 boolean hasWhitespaces = false; 1606 boolean hasComment = false; 1607 boolean hasLineComment = false; 1608 while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { 1609 switch(this.currentToken) { 1610 case TerminalTokens.TokenNameWHITESPACE : 1611 int count = 0; 1612 char[] whiteSpaces = this.scanner.getCurrentTokenSource(); 1613 for (int i = 0, max = whiteSpaces.length; i < max; i++) { 1614 switch(whiteSpaces[i]) { 1615 case '\r' : 1616 if ((i + 1) < max) { 1617 if (whiteSpaces[i + 1] == '\n') { 1618 i++; 1619 } 1620 } 1621 count++; 1622 break; 1623 case '\n' : 1624 count++; 1625 } 1626 } 1627 if (hasLineComment) { 1628 if (count >= 1) { 1629 currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); 1630 this.preserveEmptyLines(count, currentTokenStartPosition); 1631 addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition()); 1632 this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1); 1633 return; 1634 } else { 1635 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1636 return; 1637 } 1638 } else if (count >= 1) { 1639 if (hasComment) { 1640 this.printNewLine(this.scanner.getCurrentTokenStartPosition()); 1641 } 1642 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1643 return; 1644 } else { 1645 hasWhitespaces = true; 1646 currentTokenStartPosition = this.scanner.currentPosition; 1647 addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); 1648 } 1649 break; 1650 case TerminalTokens.TokenNameCOMMENT_LINE : 1651 if (hasWhitespaces) { 1652 space(); 1653 } 1654 this.printLineComment(this.scanner.getRawTokenSource()); 1655 currentTokenStartPosition = this.scanner.currentPosition; 1656 hasLineComment = true; 1657 break; 1658 case TerminalTokens.TokenNameCOMMENT_BLOCK : 1659 if (hasWhitespaces) { 1660 space(); 1661 } 1662 this.printBlockComment(this.scanner.getRawTokenSource(), false); 1663 currentTokenStartPosition = this.scanner.currentPosition; 1664 hasComment = true; 1665 break; 1666 default : 1667 this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); 1669 return; 1670 } 1671 } 1672 } catch (InvalidInputException e) { 1673 throw new AbortFormatting(e); 1674 } 1675 } 1676 1677 void redoAlignment(AlignmentException e){ 1678 if (e.relativeDepth > 0) { e.relativeDepth--; this.currentAlignment = this.currentAlignment.enclosing; throw e; } 1683 this.resetAt(this.currentAlignment.location); 1685 this.scanner.resetTo(this.currentAlignment.location.inputOffset, this.scanner.eofPosition); 1686 this.currentAlignment.chunkKind = 0; 1688 } 1689 1690 void redoMemberAlignment(AlignmentException e){ 1691 this.resetAt(this.memberAlignment.location); 1693 this.scanner.resetTo(this.memberAlignment.location.inputOffset, this.scanner.eofPosition); 1694 this.memberAlignment.chunkKind = 0; 1696 } 1697 1698 public void reset() { 1699 this.checkLineWrapping = true; 1700 this.line = 0; 1701 this.column = 1; 1702 this.editsIndex = 0; 1703 this.nlsTagCounter = 0; 1704 } 1705 1706 private void resetAt(Location location) { 1707 this.line = location.outputLine; 1708 this.column = location.outputColumn; 1709 this.indentationLevel = location.outputIndentationLevel; 1710 this.numberOfIndentations = location.numberOfIndentations; 1711 this.lastNumberOfNewLines = location.lastNumberOfNewLines; 1712 this.needSpace = location.needSpace; 1713 this.pendingSpace = location.pendingSpace; 1714 this.editsIndex = location.editsIndex; 1715 this.nlsTagCounter = location.nlsTagCounter; 1716 if (this.editsIndex > 0) { 1717 this.edits[this.editsIndex - 1] = location.textEdit; 1718 } 1719 this.formatter.lastLocalDeclarationSourceStart = location.lastLocalDeclarationSourceStart; 1720 } 1721 1722 private void resize() { 1723 System.arraycopy(this.edits, 0, (this.edits = new OptimizedReplaceEdit[this.editsIndex * 2]), 0, this.editsIndex); 1724 } 1725 1726 public void space() { 1727 if (!this.needSpace) return; 1728 this.lastNumberOfNewLines = 0; 1729 this.pendingSpace = true; 1730 this.column++; 1731 this.needSpace = false; 1732 } 1733 1734 public String toString() { 1735 StringBuffer stringBuffer = new StringBuffer (); 1736 stringBuffer 1737 .append("(page width = " + this.pageWidth + ") - (tabChar = "); switch(this.tabChar) { 1739 case DefaultCodeFormatterOptions.TAB : 1740 stringBuffer.append("TAB"); break; 1742 case DefaultCodeFormatterOptions.SPACE : 1743 stringBuffer.append("SPACE"); break; 1745 default : 1746 stringBuffer.append("MIXED"); } 1748 stringBuffer 1749 .append(") - (tabSize = " + this.tabLength + ")") .append(this.lineSeparator) 1751 .append("(line = " + this.line + ") - (column = " + this.column + ") - (identationLevel = " + this.indentationLevel + ")") .append(this.lineSeparator) 1753 .append("(needSpace = " + this.needSpace + ") - (lastNumberOfNewLines = " + this.lastNumberOfNewLines + ") - (checkLineWrapping = " + this.checkLineWrapping + ")") .append(this.lineSeparator) 1755 .append("==================================================================================") .append(this.lineSeparator); 1757 printRule(stringBuffer); 1758 return stringBuffer.toString(); 1759 } 1760 1761 public void unIndent() { 1762 this.indentationLevel -= this.indentationSize; 1763 this.numberOfIndentations--; 1764 } 1765} 1766 | Popular Tags |