| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.formatter; |
| |
| import java.util.Arrays; |
| import java.util.Map; |
| |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.parser.Scanner; |
| import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
| import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil; |
| import org.eclipse.jdt.internal.core.util.RecordedParsingInformation; |
| import org.eclipse.jdt.internal.formatter.align.Alignment; |
| import org.eclipse.jdt.internal.formatter.align.AlignmentException; |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.ReplaceEdit; |
| import org.eclipse.text.edits.TextEdit; |
| |
| /** |
| * This class is responsible for dumping formatted source |
| * @since 2.1 |
| */ |
| public class Scribe { |
| private static final String EMPTY_STRING = ""; //$NON-NLS-1$ |
| |
| private static final int INITIAL_SIZE = 100; |
| |
| private boolean checkLineWrapping; |
| /** one-based column */ |
| public int column; |
| private int[][] commentPositions; |
| |
| // Most specific alignment. |
| public Alignment currentAlignment; |
| public int currentToken; |
| |
| // edits management |
| private OptimizedReplaceEdit[] edits; |
| public int editsIndex; |
| |
| public CodeFormatterVisitor formatter; |
| public int indentationLevel; |
| public int lastNumberOfNewLines; |
| public int line; |
| |
| private int[] lineEnds; |
| private String lineSeparator; |
| public Alignment memberAlignment; |
| public boolean needSpace = false; |
| |
| public int nlsTagCounter; |
| public int pageWidth; |
| public boolean pendingSpace = false; |
| |
| public Scanner scanner; |
| public int scannerEndPosition; |
| public int tabLength; |
| public int indentationSize; |
| private int textRegionEnd; |
| private int textRegionStart; |
| public int tabChar; |
| public int numberOfIndentations; |
| private boolean useTabsOnlyForLeadingIndents; |
| |
| Scribe(CodeFormatterVisitor formatter, Map settings, int offset, int length, CodeSnippetParsingUtil codeSnippetParsingUtil) { |
| if (settings != null) { |
| Object sourceLevelOption = settings.get(JavaCore.COMPILER_SOURCE); |
| long sourceLevel = ClassFileConstants.JDK1_3; |
| if (JavaCore.VERSION_1_4.equals(sourceLevelOption)) { |
| sourceLevel = ClassFileConstants.JDK1_4; |
| } else if (JavaCore.VERSION_1_5.equals(sourceLevelOption)) { |
| sourceLevel = ClassFileConstants.JDK1_5; |
| } |
| this.scanner = new Scanner(true, true, false/*nls*/, sourceLevel/*sourceLevel*/, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); |
| } else { |
| this.scanner = new Scanner(true, true, false/*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); |
| } |
| this.formatter = formatter; |
| this.pageWidth = formatter.preferences.page_width; |
| this.tabLength = formatter.preferences.tab_size; |
| this.indentationLevel= 0; // initialize properly |
| this.numberOfIndentations = 0; |
| this.useTabsOnlyForLeadingIndents = formatter.preferences.use_tabs_only_for_leading_indentations; |
| this.tabChar = formatter.preferences.tab_char; |
| if (this.tabChar == DefaultCodeFormatterOptions.MIXED) { |
| this.indentationSize = formatter.preferences.indentation_size; |
| } else { |
| this.indentationSize = this.tabLength; |
| } |
| this.lineSeparator = formatter.preferences.line_separator; |
| this.indentationLevel = formatter.preferences.initial_indentation_level * this.indentationSize; |
| this.textRegionStart = offset; |
| this.textRegionEnd = offset + length - 1; |
| if (codeSnippetParsingUtil != null) { |
| final RecordedParsingInformation information = codeSnippetParsingUtil.recordedParsingInformation; |
| if (information != null) { |
| this.lineEnds = information.lineEnds; |
| this.commentPositions = information.commentPositions; |
| } |
| } |
| reset(); |
| } |
| |
| private final void addDeleteEdit(int start, int end) { |
| if (this.edits.length == this.editsIndex) { |
| // resize |
| resize(); |
| } |
| addOptimizedReplaceEdit(start, end - start + 1, EMPTY_STRING); //$NON-NLS-1$ |
| } |
| |
| public final void addInsertEdit(int insertPosition, String insertedString) { |
| if (this.edits.length == this.editsIndex) { |
| // resize |
| resize(); |
| } |
| addOptimizedReplaceEdit(insertPosition, 0, insertedString); |
| } |
| |
| private final void addOptimizedReplaceEdit(int offset, int length, String replacement) { |
| if (this.editsIndex > 0) { |
| // try to merge last two edits |
| final OptimizedReplaceEdit previous = this.edits[this.editsIndex-1]; |
| final int previousOffset = previous.offset; |
| final int previousLength = previous.length; |
| final int endOffsetOfPreviousEdit = previousOffset + previousLength; |
| final int replacementLength = replacement.length(); |
| final String previousReplacement = previous.replacement; |
| final int previousReplacementLength = previousReplacement.length(); |
| if (previousOffset == offset && previousLength == length && (replacementLength == 0 || previousReplacementLength == 0)) { |
| if (this.currentAlignment != null) { |
| final Location location = this.currentAlignment.location; |
| if (location.editsIndex == this.editsIndex) { |
| location.editsIndex--; |
| location.textEdit = previous; |
| } |
| } |
| this.editsIndex--; |
| return; |
| } |
| if (endOffsetOfPreviousEdit == offset) { |
| if (length != 0) { |
| if (replacementLength != 0) { |
| this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement + replacement); |
| } else if (previousLength + length == previousReplacementLength) { |
| // check the characters. If they are identical, we can get rid of the previous edit |
| boolean canBeRemoved = true; |
| loop: for (int i = previousOffset; i < previousOffset + previousReplacementLength; i++) { |
| if (scanner.source[i] != previousReplacement.charAt(i - previousOffset)) { |
| this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousReplacementLength, previousReplacement); |
| canBeRemoved = false; |
| break loop; |
| } |
| } |
| if (canBeRemoved) { |
| if (this.currentAlignment != null) { |
| final Location location = this.currentAlignment.location; |
| if (location.editsIndex == this.editsIndex) { |
| location.editsIndex--; |
| location.textEdit = previous; |
| } |
| } |
| this.editsIndex--; |
| } |
| } else { |
| this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement); |
| } |
| } else { |
| if (replacementLength != 0) { |
| this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength, previousReplacement + replacement); |
| } |
| } |
| } else { |
| this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement); |
| } |
| } else { |
| this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement); |
| } |
| } |
| |
| public final void addReplaceEdit(int start, int end, String replacement) { |
| if (this.edits.length == this.editsIndex) { |
| // resize |
| resize(); |
| } |
| addOptimizedReplaceEdit(start, end - start + 1, replacement); |
| } |
| |
| public void alignFragment(Alignment alignment, int fragmentIndex){ |
| alignment.fragmentIndex = fragmentIndex; |
| alignment.checkColumn(); |
| alignment.performFragmentEffect(); |
| } |
| |
| public void checkNLSTag(int sourceStart) { |
| if (hasNLSTag(sourceStart)) { |
| this.nlsTagCounter++; |
| } |
| } |
| public void consumeNextToken() { |
| printComment(); |
| try { |
| this.currentToken = this.scanner.getNextToken(); |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| public Alignment createAlignment(String name, int mode, int count, int sourceRestart){ |
| return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart); |
| } |
| |
| public Alignment createAlignment(String name, int mode, int count, int sourceRestart, boolean adjust){ |
| return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, adjust); |
| } |
| |
| public Alignment createAlignment(String name, int mode, int tieBreakRule, int count, int sourceRestart){ |
| return createAlignment(name, mode, tieBreakRule, count, sourceRestart, this.formatter.preferences.continuation_indentation, false); |
| } |
| |
| public Alignment createAlignment(String name, int mode, int count, int sourceRestart, int continuationIndent, boolean adjust){ |
| return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, continuationIndent, adjust); |
| } |
| |
| public Alignment createAlignment(String name, int mode, int tieBreakRule, int count, int sourceRestart, int continuationIndent, boolean adjust){ |
| Alignment alignment = new Alignment(name, mode, tieBreakRule, this, count, sourceRestart, continuationIndent); |
| // adjust break indentation |
| if (adjust && this.memberAlignment != null) { |
| Alignment current = this.memberAlignment; |
| while (current.enclosing != null) { |
| current = current.enclosing; |
| } |
| if ((current.mode & Alignment.M_MULTICOLUMN) != 0) { |
| final int indentSize = this.indentationSize; |
| switch(current.chunkKind) { |
| case Alignment.CHUNK_METHOD : |
| case Alignment.CHUNK_TYPE : |
| if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { |
| alignment.breakIndentationLevel = this.indentationLevel + indentSize; |
| } else { |
| alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize; |
| } |
| alignment.update(); |
| break; |
| case Alignment.CHUNK_FIELD : |
| if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { |
| alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize; |
| } else { |
| alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize; |
| } |
| alignment.update(); |
| break; |
| } |
| } else { |
| switch(current.mode & Alignment.SPLIT_MASK) { |
| case Alignment.M_COMPACT_SPLIT : |
| case Alignment.M_COMPACT_FIRST_BREAK_SPLIT : |
| case Alignment.M_NEXT_PER_LINE_SPLIT : |
| case Alignment.M_NEXT_SHIFTED_SPLIT : |
| case Alignment.M_ONE_PER_LINE_SPLIT : |
| final int indentSize = this.indentationSize; |
| switch(current.chunkKind) { |
| case Alignment.CHUNK_METHOD : |
| case Alignment.CHUNK_TYPE : |
| if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { |
| alignment.breakIndentationLevel = this.indentationLevel + indentSize; |
| } else { |
| alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize; |
| } |
| alignment.update(); |
| break; |
| case Alignment.CHUNK_FIELD : |
| if ((mode & Alignment.M_INDENT_BY_ONE) != 0) { |
| alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize; |
| } else { |
| alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize; |
| } |
| alignment.update(); |
| break; |
| } |
| break; |
| } |
| } |
| } |
| return alignment; |
| } |
| |
| public Alignment createMemberAlignment(String name, int mode, int count, int sourceRestart) { |
| Alignment mAlignment = createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart); |
| mAlignment.breakIndentationLevel = this.indentationLevel; |
| return mAlignment; |
| } |
| |
| public void enterAlignment(Alignment alignment){ |
| alignment.enclosing = this.currentAlignment; |
| alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart; |
| this.currentAlignment = alignment; |
| } |
| |
| public void enterMemberAlignment(Alignment alignment) { |
| alignment.enclosing = this.memberAlignment; |
| alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart; |
| this.memberAlignment = alignment; |
| } |
| |
| public void exitAlignment(Alignment alignment, boolean discardAlignment){ |
| Alignment current = this.currentAlignment; |
| while (current != null){ |
| if (current == alignment) break; |
| current = current.enclosing; |
| } |
| if (current == null) { |
| throw new AbortFormatting("could not find matching alignment: "+alignment); //$NON-NLS-1$ |
| } |
| this.indentationLevel = alignment.location.outputIndentationLevel; |
| this.numberOfIndentations = alignment.location.numberOfIndentations; |
| this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart; |
| if (discardAlignment){ |
| this.currentAlignment = alignment.enclosing; |
| } |
| } |
| |
| public void exitMemberAlignment(Alignment alignment){ |
| Alignment current = this.memberAlignment; |
| while (current != null){ |
| if (current == alignment) break; |
| current = current.enclosing; |
| } |
| if (current == null) { |
| throw new AbortFormatting("could not find matching alignment: "+alignment); //$NON-NLS-1$ |
| } |
| this.indentationLevel = current.location.outputIndentationLevel; |
| this.numberOfIndentations = current.location.numberOfIndentations; |
| this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart; |
| this.memberAlignment = current.enclosing; |
| } |
| |
| public Alignment getAlignment(String name){ |
| if (this.currentAlignment != null) { |
| return this.currentAlignment.getAlignment(name); |
| } |
| return null; |
| } |
| |
| /** |
| * Answer actual indentation level based on true column position |
| * @return int |
| */ |
| public int getColumnIndentationLevel() { |
| return this.column - 1; |
| } |
| |
| public final int getCommentIndex(int position) { |
| if (this.commentPositions == null) |
| return -1; |
| int length = this.commentPositions.length; |
| if (length == 0) { |
| return -1; |
| } |
| int g = 0, d = length - 1; |
| int m = 0; |
| while (g <= d) { |
| m = (g + d) / 2; |
| int bound = this.commentPositions[m][1]; |
| if (bound < 0) { |
| bound = -bound; |
| } |
| if (bound < position) { |
| g = m + 1; |
| } else if (bound > position) { |
| d = m - 1; |
| } else { |
| return m; |
| } |
| } |
| return -(g + 1); |
| } |
| |
| public String getEmptyLines(int linesNumber) { |
| if (this.nlsTagCounter > 0) { |
| return EMPTY_STRING; |
| } |
| StringBuffer buffer = new StringBuffer(); |
| if (lastNumberOfNewLines == 0) { |
| linesNumber++; // add an extra line breaks |
| for (int i = 0; i < linesNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += linesNumber; |
| line += linesNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } else if (lastNumberOfNewLines == 1) { |
| for (int i = 0; i < linesNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += linesNumber; |
| line += linesNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } else { |
| if ((lastNumberOfNewLines - 1) >= linesNumber) { |
| // there is no need to add new lines |
| return EMPTY_STRING; |
| } |
| final int realNewLineNumber = linesNumber - lastNumberOfNewLines + 1; |
| for (int i = 0; i < realNewLineNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += realNewLineNumber; |
| line += realNewLineNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } |
| return String.valueOf(buffer); |
| } |
| |
| public OptimizedReplaceEdit getLastEdit() { |
| if (this.editsIndex > 0) { |
| return this.edits[this.editsIndex - 1]; |
| } |
| return null; |
| } |
| |
| public final int getLineEnd(int lineNumber) { |
| if (this.lineEnds == null) |
| return -1; |
| if (lineNumber >= this.lineEnds.length + 1) |
| return this.scannerEndPosition; |
| if (lineNumber <= 0) |
| return -1; |
| return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line |
| } |
| |
| Alignment getMemberAlignment() { |
| return this.memberAlignment; |
| } |
| |
| public String getNewLine() { |
| if (this.nlsTagCounter > 0) { |
| return EMPTY_STRING; |
| } |
| if (lastNumberOfNewLines >= 1) { |
| column = 1; // ensure that the scribe is at the beginning of a new line |
| return EMPTY_STRING; |
| } |
| line++; |
| lastNumberOfNewLines = 1; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| return this.lineSeparator; |
| } |
| |
| /** |
| * Answer next indentation level based on column estimated position |
| * (if column is not indented, then use indentationLevel) |
| */ |
| public int getNextIndentationLevel(int someColumn) { |
| int indent = someColumn - 1; |
| if (indent == 0) |
| return this.indentationLevel; |
| if (this.tabChar == DefaultCodeFormatterOptions.TAB) { |
| if (this.useTabsOnlyForLeadingIndents) { |
| return indent; |
| } |
| int rem = indent % this.indentationSize; |
| int addition = rem == 0 ? 0 : this.indentationSize - rem; // round to superior |
| return indent + addition; |
| } else { |
| return indent; |
| } |
| } |
| |
| private String getPreserveEmptyLines(int count) { |
| if (count > 0) { |
| if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { |
| int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve); |
| return this.getEmptyLines(linesToPreserve); |
| } else { |
| return getNewLine(); |
| } |
| } |
| return EMPTY_STRING; |
| } |
| |
| public TextEdit getRootEdit() { |
| MultiTextEdit edit = null; |
| int length = this.textRegionEnd - this.textRegionStart + 1; |
| if (this.textRegionStart <= 0) { |
| if (length <= 0) { |
| edit = new MultiTextEdit(0, 0); |
| } else { |
| edit = new MultiTextEdit(0, this.textRegionEnd + 1); |
| } |
| } else { |
| edit = new MultiTextEdit(this.textRegionStart, this.textRegionEnd - this.textRegionStart + 1); |
| } |
| for (int i= 0, max = this.editsIndex; i < max; i++) { |
| OptimizedReplaceEdit currentEdit = edits[i]; |
| if (isValidEdit(currentEdit)) { |
| edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement)); |
| } |
| } |
| this.edits = null; |
| return edit; |
| } |
| |
| public void handleLineTooLong() { |
| // search for closest breakable alignment, using tiebreak rules |
| // look for outermost breakable one |
| int relativeDepth = 0, outerMostDepth = -1; |
| Alignment targetAlignment = this.currentAlignment; |
| while (targetAlignment != null){ |
| if (targetAlignment.tieBreakRule == Alignment.R_OUTERMOST && targetAlignment.couldBreak()){ |
| outerMostDepth = relativeDepth; |
| } |
| targetAlignment = targetAlignment.enclosing; |
| relativeDepth++; |
| } |
| if (outerMostDepth >= 0) { |
| throw new AlignmentException(AlignmentException.LINE_TOO_LONG, outerMostDepth); |
| } |
| // look for innermost breakable one |
| relativeDepth = 0; |
| targetAlignment = this.currentAlignment; |
| while (targetAlignment != null){ |
| if (targetAlignment.couldBreak()){ |
| throw new AlignmentException(AlignmentException.LINE_TOO_LONG, relativeDepth); |
| } |
| targetAlignment = targetAlignment.enclosing; |
| relativeDepth++; |
| } |
| // did not find any breakable location - proceed |
| } |
| |
| /* |
| * Check if there is a NLS tag on this line. If yes, return true, returns false otherwise. |
| */ |
| private boolean hasNLSTag(int sourceStart) { |
| // search the last comment where commentEnd < current lineEnd |
| if (this.lineEnds == null) return false; |
| int index = Arrays.binarySearch(this.lineEnds, sourceStart); |
| int currentLineEnd = this.getLineEnd(-index); |
| if (currentLineEnd != -1) { |
| int commentIndex = getCommentIndex(currentLineEnd); |
| if (commentIndex < 0) { |
| commentIndex = -commentIndex - 2; |
| } |
| if (commentIndex >= 0 && commentIndex < this.commentPositions.length) { |
| int start = this.commentPositions[commentIndex][0]; |
| if (start < 0) { |
| start = -start; |
| // check that we are on the same line |
| int lineIndexForComment = Arrays.binarySearch(this.lineEnds, start); |
| if (lineIndexForComment == index) { |
| return indexOf(Scanner.TAG_PREFIX.toCharArray(), this.scanner.source, start, currentLineEnd) != -1; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| public void indent() { |
| this.indentationLevel += this.indentationSize; |
| this.numberOfIndentations++; |
| } |
| |
| private int indexOf(char[] toBeFound, char[] source, int start, int end) { |
| if (toBeFound == null || source == null) { |
| throw new IllegalArgumentException(); |
| } |
| int toBeFoundLength = toBeFound.length; |
| if (end < start || (end - start + 1) < toBeFoundLength) { |
| return -1; |
| } |
| int indexInSource = 0; |
| for (int i = start; i < end; i++) { |
| if (source[i] == toBeFound[indexInSource]) { |
| int j = i + 1; |
| indexInSource++; |
| loop: for (; j < end && indexInSource < toBeFoundLength; j++) { |
| if (toBeFound[indexInSource] != source[j]) { |
| break loop; |
| } |
| indexInSource++; |
| } |
| if (j == i + toBeFoundLength) { |
| return i; |
| } |
| indexInSource = 0; |
| } |
| } |
| return -1; |
| } |
| /** |
| * @param compilationUnitSource |
| */ |
| public void initializeScanner(char[] compilationUnitSource) { |
| this.scanner.setSource(compilationUnitSource); |
| this.scannerEndPosition = compilationUnitSource.length; |
| this.scanner.resetTo(0, this.scannerEndPosition); |
| this.edits = new OptimizedReplaceEdit[INITIAL_SIZE]; |
| } |
| |
| private boolean isValidEdit(OptimizedReplaceEdit edit) { |
| final int editLength= edit.length; |
| final int editReplacementLength= edit.replacement.length(); |
| final int editOffset= edit.offset; |
| if (editLength != 0) { |
| if (this.textRegionStart <= editOffset && (editOffset + editLength - 1) <= this.textRegionEnd) { |
| if (editReplacementLength != 0 && editLength == editReplacementLength) { |
| for (int i = editOffset, max = editOffset + editLength; i < max; i++) { |
| if (scanner.source[i] != edit.replacement.charAt(i - editOffset)) { |
| return true; |
| } |
| } |
| return false; |
| } else { |
| return true; |
| } |
| } else if (editOffset + editLength == this.textRegionStart) { |
| int i = editOffset; |
| for (int max = editOffset + editLength; i < max; i++) { |
| int replacementStringIndex = i - editOffset; |
| if (replacementStringIndex >= editReplacementLength || scanner.source[i] != edit.replacement.charAt(replacementStringIndex)) { |
| break; |
| } |
| } |
| if (i - editOffset != editReplacementLength && i != editOffset + editLength - 1) { |
| edit.offset = textRegionStart; |
| edit.length = 0; |
| edit.replacement = edit.replacement.substring(i - editOffset); |
| return true; |
| } |
| } |
| } else if (this.textRegionStart <= editOffset && editOffset <= this.textRegionEnd) { |
| return true; |
| } else if (editOffset == this.scannerEndPosition && editOffset == this.textRegionEnd + 1) { |
| return true; |
| } |
| return false; |
| } |
| |
| private void preserveEmptyLines(int count, int insertPosition) { |
| if (count > 0) { |
| if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { |
| int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve); |
| this.printEmptyLines(linesToPreserve, insertPosition); |
| } else { |
| printNewLine(insertPosition); |
| } |
| } |
| } |
| |
| private void print(char[] s, boolean considerSpaceIfAny) { |
| if (checkLineWrapping && s.length + column > this.pageWidth) { |
| handleLineTooLong(); |
| } |
| this.lastNumberOfNewLines = 0; |
| printIndentationIfNecessary(); |
| if (considerSpaceIfAny) { |
| this.space(); |
| } |
| if (this.pendingSpace) { |
| this.addInsertEdit(this.scanner.getCurrentTokenStartPosition(), " "); //$NON-NLS-1$ |
| } |
| this.pendingSpace = false; |
| this.needSpace = false; |
| column += s.length; |
| needSpace = true; |
| } |
| |
| private void printBlockComment(char[] s, boolean isJavadoc) { |
| int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); |
| int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1; |
| |
| this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1); |
| int currentCharacter; |
| boolean isNewLine = false; |
| int start = currentTokenStartPosition; |
| int nextCharacterStart = currentTokenStartPosition; |
| printIndentationIfNecessary(); |
| if (this.pendingSpace) { |
| this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$ |
| } |
| this.needSpace = false; |
| this.pendingSpace = false; |
| int previousStart = currentTokenStartPosition; |
| |
| while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) { |
| nextCharacterStart = this.scanner.currentPosition; |
| |
| switch(currentCharacter) { |
| case '\r' : |
| start = previousStart; |
| isNewLine = true; |
| if (this.scanner.getNextChar('\n')) { |
| currentCharacter = '\n'; |
| nextCharacterStart = this.scanner.currentPosition; |
| } |
| break; |
| case '\n' : |
| start = previousStart; |
| isNewLine = true; |
| break; |
| default: |
| if (isNewLine) { |
| if (Character.isWhitespace((char) currentCharacter)) { |
| int previousStartPosition = this.scanner.currentPosition; |
| while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && Character.isWhitespace((char) currentCharacter)) { |
| previousStart = nextCharacterStart; |
| previousStartPosition = this.scanner.currentPosition; |
| currentCharacter = this.scanner.getNextChar(); |
| nextCharacterStart = this.scanner.currentPosition; |
| } |
| if (currentCharacter == '\r' || currentCharacter == '\n') { |
| nextCharacterStart = previousStartPosition; |
| } |
| } |
| this.column = 1; |
| this.line++; |
| |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append(this.lineSeparator); |
| printIndentationIfNecessary(buffer); |
| buffer.append(' '); |
| |
| addReplaceEdit(start, previousStart - 1, String.valueOf(buffer)); |
| } else { |
| this.column += (nextCharacterStart - previousStart); |
| } |
| isNewLine = false; |
| } |
| previousStart = nextCharacterStart; |
| this.scanner.currentPosition = nextCharacterStart; |
| } |
| this.lastNumberOfNewLines = 0; |
| needSpace = false; |
| this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1); |
| if (isJavadoc) { |
| printNewLine(); |
| } |
| } |
| |
| public void printEndOfCompilationUnit() { |
| try { |
| // if we have a space between two tokens we ensure it will be dumped in the formatted string |
| int currentTokenStartPosition = this.scanner.currentPosition; |
| boolean hasComment = false; |
| boolean hasLineComment = false; |
| boolean hasWhitespace = false; |
| int count = 0; |
| while (true) { |
| this.currentToken = this.scanner.getNextToken(); |
| switch(this.currentToken) { |
| case TerminalTokens.TokenNameWHITESPACE : |
| char[] whiteSpaces = this.scanner.getCurrentTokenSource(); |
| count = 0; |
| for (int i = 0, max = whiteSpaces.length; i < max; i++) { |
| switch(whiteSpaces[i]) { |
| case '\r' : |
| if ((i + 1) < max) { |
| if (whiteSpaces[i + 1] == '\n') { |
| i++; |
| } |
| } |
| count++; |
| break; |
| case '\n' : |
| count++; |
| } |
| } |
| if (count == 0) { |
| hasWhitespace = true; |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else if (hasComment) { |
| if (count == 1) { |
| this.printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } else { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else if (hasLineComment) { |
| this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition()); |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else { |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_LINE : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printCommentLine(this.scanner.getRawTokenSource()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = true; |
| count = 0; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_BLOCK : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printBlockComment(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = false; |
| hasComment = true; |
| count = 0; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_JAVADOC : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printBlockComment(this.scanner.getRawTokenSource(), true); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = false; |
| hasComment = true; |
| count = 0; |
| break; |
| case TerminalTokens.TokenNameSEMICOLON : |
| char[] currentTokenSource = this.scanner.getRawTokenSource(); |
| this.print(currentTokenSource, this.formatter.preferences.insert_space_before_semicolon); |
| break; |
| case TerminalTokens.TokenNameEOF : |
| if (count >= 1 || this.formatter.preferences.insert_new_line_at_end_of_file_if_missing) { |
| this.printNewLine(this.scannerEndPosition); |
| } |
| return; |
| default : |
| // step back one token |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| public void printComment() { |
| try { |
| // if we have a space between two tokens we ensure it will be dumped in the formatted string |
| int currentTokenStartPosition = this.scanner.currentPosition; |
| boolean hasComment = false; |
| boolean hasLineComment = false; |
| boolean hasWhitespace = false; |
| int count = 0; |
| while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { |
| switch(this.currentToken) { |
| case TerminalTokens.TokenNameWHITESPACE : |
| char[] whiteSpaces = this.scanner.getCurrentTokenSource(); |
| count = 0; |
| for (int i = 0, max = whiteSpaces.length; i < max; i++) { |
| switch(whiteSpaces[i]) { |
| case '\r' : |
| if ((i + 1) < max) { |
| if (whiteSpaces[i + 1] == '\n') { |
| i++; |
| } |
| } |
| count++; |
| break; |
| case '\n' : |
| count++; |
| } |
| } |
| if (count == 0) { |
| hasWhitespace = true; |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else if (hasComment) { |
| if (count == 1) { |
| this.printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } else { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else if (hasLineComment) { |
| this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition()); |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } else if (count != 0 && this.formatter.preferences.number_of_empty_lines_to_preserve != 0) { |
| addReplaceEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition(), this.getPreserveEmptyLines(count - 1)); |
| } else { |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_LINE : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printCommentLine(this.scanner.getRawTokenSource()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = true; |
| count = 0; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_BLOCK : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printBlockComment(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = false; |
| hasComment = true; |
| count = 0; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_JAVADOC : |
| if (count >= 1) { |
| if (count > 1) { |
| preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition()); |
| } else if (count == 1) { |
| printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| } else if (hasWhitespace) { |
| space(); |
| } |
| hasWhitespace = false; |
| this.printBlockComment(this.scanner.getRawTokenSource(), true); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = false; |
| hasComment = true; |
| count = 0; |
| break; |
| default : |
| // step back one token |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| private void printCommentLine(char[] s) { |
| int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); |
| int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1; |
| if (indexOf(Scanner.TAG_PREFIX.toCharArray(), this.scanner.source, currentTokenStartPosition, currentTokenEndPosition) != -1) { |
| this.nlsTagCounter = 0; |
| } |
| this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1); |
| int currentCharacter; |
| int start = currentTokenStartPosition; |
| int nextCharacterStart = currentTokenStartPosition; |
| printIndentationIfNecessary(); |
| if (this.pendingSpace) { |
| this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$ |
| } |
| this.needSpace = false; |
| this.pendingSpace = false; |
| int previousStart = currentTokenStartPosition; |
| |
| loop: while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) { |
| nextCharacterStart = this.scanner.currentPosition; |
| |
| switch(currentCharacter) { |
| case '\r' : |
| start = previousStart; |
| break loop; |
| case '\n' : |
| start = previousStart; |
| break loop; |
| } |
| previousStart = nextCharacterStart; |
| } |
| if (start != currentTokenStartPosition) { |
| addReplaceEdit(start, currentTokenEndPosition - 1, lineSeparator); |
| } |
| line++; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| lastNumberOfNewLines = 1; |
| // realign to the proper value |
| if (this.currentAlignment != null) { |
| if (this.memberAlignment != null) { |
| // select the last alignment |
| if (this.currentAlignment.location.inputOffset > this.memberAlignment.location.inputOffset) { |
| this.indentationLevel = Math.max(this.indentationLevel, this.currentAlignment.breakIndentationLevel); |
| } else { |
| this.indentationLevel = Math.max(this.indentationLevel, this.memberAlignment.breakIndentationLevel); |
| } |
| } else { |
| this.indentationLevel = Math.max(this.indentationLevel, this.currentAlignment.breakIndentationLevel); |
| } |
| } |
| this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1); |
| } |
| public void printEmptyLines(int linesNumber) { |
| this.printEmptyLines(linesNumber, this.scanner.getCurrentTokenEndPosition() + 1); |
| } |
| |
| private void printEmptyLines(int linesNumber, int insertPosition) { |
| if (this.nlsTagCounter > 0) { |
| return; |
| } |
| StringBuffer buffer = new StringBuffer(); |
| if (lastNumberOfNewLines == 0) { |
| linesNumber++; // add an extra line breaks |
| for (int i = 0; i < linesNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += linesNumber; |
| line += linesNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } else if (lastNumberOfNewLines == 1) { |
| for (int i = 0; i < linesNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += linesNumber; |
| line += linesNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } else { |
| if ((lastNumberOfNewLines - 1) >= linesNumber) { |
| // there is no need to add new lines |
| return; |
| } |
| final int realNewLineNumber = linesNumber - lastNumberOfNewLines + 1; |
| for (int i = 0; i < realNewLineNumber; i++) { |
| buffer.append(this.lineSeparator); |
| } |
| lastNumberOfNewLines += realNewLineNumber; |
| line += realNewLineNumber; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } |
| addInsertEdit(insertPosition, buffer.toString()); |
| } |
| |
| private void printIndentationIfNecessary() { |
| StringBuffer buffer = new StringBuffer(); |
| printIndentationIfNecessary(buffer); |
| if (buffer.length() > 0) { |
| addInsertEdit(this.scanner.getCurrentTokenStartPosition(), buffer.toString()); |
| this.pendingSpace = false; |
| } |
| } |
| |
| private void printIndentationIfNecessary(StringBuffer buffer) { |
| switch(this.tabChar) { |
| case DefaultCodeFormatterOptions.TAB : |
| boolean useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents; |
| int numberOfLeadingIndents = this.numberOfIndentations; |
| int indentationsAsTab = 0; |
| if (useTabsForLeadingIndents) { |
| while (this.column <= this.indentationLevel) { |
| if (indentationsAsTab < numberOfLeadingIndents) { |
| buffer.append('\t'); |
| indentationsAsTab++; |
| this.lastNumberOfNewLines = 0; |
| int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space |
| this.column += complement; |
| this.needSpace = false; |
| } else { |
| buffer.append(' '); |
| this.column++; |
| this.needSpace = false; |
| } |
| } |
| } else { |
| while (this.column <= this.indentationLevel) { |
| buffer.append('\t'); |
| this.lastNumberOfNewLines = 0; |
| int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space |
| this.column += complement; |
| this.needSpace = false; |
| } |
| } |
| break; |
| case DefaultCodeFormatterOptions.SPACE : |
| while (this.column <= this.indentationLevel) { |
| buffer.append(' '); |
| this.column++; |
| this.needSpace = false; |
| } |
| break; |
| case DefaultCodeFormatterOptions.MIXED : |
| useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents; |
| numberOfLeadingIndents = this.numberOfIndentations; |
| indentationsAsTab = 0; |
| if (useTabsForLeadingIndents) { |
| final int columnForLeadingIndents = numberOfLeadingIndents * this.indentationSize; |
| while (this.column <= this.indentationLevel) { |
| if (this.column <= columnForLeadingIndents) { |
| if ((this.column - 1 + this.tabLength) <= this.indentationLevel) { |
| buffer.append('\t'); |
| this.column += this.tabLength; |
| } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) { |
| // print one indentation |
| for (int i = 0, max = this.indentationSize; i < max; i++) { |
| buffer.append(' '); |
| this.column++; |
| } |
| } else { |
| buffer.append(' '); |
| this.column++; |
| } |
| } else { |
| for (int i = this.column, max = this.indentationLevel; i <= max; i++) { |
| buffer.append(' '); |
| this.column++; |
| } |
| } |
| this.needSpace = false; |
| } |
| } else { |
| while (this.column <= this.indentationLevel) { |
| if ((this.column - 1 + this.tabLength) <= this.indentationLevel) { |
| buffer.append('\t'); |
| this.column += this.tabLength; |
| } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) { |
| // print one indentation |
| for (int i = 0, max = this.indentationSize; i < max; i++) { |
| buffer.append(' '); |
| this.column++; |
| } |
| } else { |
| buffer.append(' '); |
| this.column++; |
| } |
| this.needSpace = false; |
| } |
| } |
| break; |
| } |
| } |
| |
| public void printModifiers(Annotation[] annotations, ASTVisitor visitor) { |
| try { |
| int annotationsLength = annotations != null ? annotations.length : 0; |
| int annotationsIndex = 0; |
| boolean isFirstModifier = true; |
| int currentTokenStartPosition = this.scanner.currentPosition; |
| boolean hasComment = false; |
| boolean hasModifiers = false; |
| while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { |
| switch(this.currentToken) { |
| case TerminalTokens.TokenNamepublic : |
| case TerminalTokens.TokenNameprotected : |
| case TerminalTokens.TokenNameprivate : |
| case TerminalTokens.TokenNamestatic : |
| case TerminalTokens.TokenNameabstract : |
| case TerminalTokens.TokenNamefinal : |
| case TerminalTokens.TokenNamenative : |
| case TerminalTokens.TokenNamesynchronized : |
| case TerminalTokens.TokenNametransient : |
| case TerminalTokens.TokenNamevolatile : |
| case TerminalTokens.TokenNamestrictfp : |
| hasModifiers = true; |
| this.print(this.scanner.getRawTokenSource(), !isFirstModifier); |
| isFirstModifier = false; |
| currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); |
| break; |
| case TerminalTokens.TokenNameAT : |
| hasModifiers = true; |
| if (!isFirstModifier) { |
| this.space(); |
| } |
| this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1); |
| if (annotationsIndex < annotationsLength) { |
| annotations[annotationsIndex++].traverse(visitor, (BlockScope) null); |
| if (this.formatter.preferences.insert_new_line_after_annotation) { |
| this.printNewLine(); |
| } |
| } else { |
| return; |
| } |
| isFirstModifier = false; |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_BLOCK : |
| this.printBlockComment(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasComment = true; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_JAVADOC : |
| this.printBlockComment(this.scanner.getRawTokenSource(), true); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasComment = true; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_LINE : |
| this.printCommentLine(this.scanner.getRawTokenSource()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameWHITESPACE : |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| int count = 0; |
| char[] whiteSpaces = this.scanner.getCurrentTokenSource(); |
| for (int i = 0, max = whiteSpaces.length; i < max; i++) { |
| switch(whiteSpaces[i]) { |
| case '\r' : |
| if ((i + 1) < max) { |
| if (whiteSpaces[i + 1] == '\n') { |
| i++; |
| } |
| } |
| count++; |
| break; |
| case '\n' : |
| count++; |
| } |
| } |
| if (count >= 1 && hasComment) { |
| printNewLine(); |
| } |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasComment = false; |
| break; |
| default: |
| if (hasModifiers) { |
| this.space(); |
| } |
| // step back one token |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| public void printNewLine() { |
| if (this.nlsTagCounter > 0) { |
| return; |
| } |
| if (lastNumberOfNewLines >= 1) { |
| column = 1; // ensure that the scribe is at the beginning of a new line |
| return; |
| } |
| addInsertEdit(this.scanner.getCurrentTokenEndPosition() + 1, this.lineSeparator); |
| line++; |
| lastNumberOfNewLines = 1; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } |
| |
| public void printNewLine(int insertPosition) { |
| if (this.nlsTagCounter > 0) { |
| return; |
| } |
| if (lastNumberOfNewLines >= 1) { |
| column = 1; // ensure that the scribe is at the beginning of a new line |
| return; |
| } |
| addInsertEdit(insertPosition, this.lineSeparator); |
| line++; |
| lastNumberOfNewLines = 1; |
| column = 1; |
| needSpace = false; |
| this.pendingSpace = false; |
| } |
| |
| public void printNextToken(int expectedTokenType){ |
| printNextToken(expectedTokenType, false); |
| } |
| |
| public void printNextToken(int expectedTokenType, boolean considerSpaceIfAny){ |
| printComment(); |
| try { |
| this.currentToken = this.scanner.getNextToken(); |
| char[] currentTokenSource = this.scanner.getRawTokenSource(); |
| if (expectedTokenType != this.currentToken) { |
| throw new AbortFormatting("unexpected token type, expecting:"+expectedTokenType+", actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$ |
| } |
| this.print(currentTokenSource, considerSpaceIfAny); |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| public void printNextToken(int[] expectedTokenTypes) { |
| printNextToken(expectedTokenTypes, false); |
| } |
| |
| public void printNextToken(int[] expectedTokenTypes, boolean considerSpaceIfAny){ |
| printComment(); |
| try { |
| this.currentToken = this.scanner.getNextToken(); |
| char[] currentTokenSource = this.scanner.getRawTokenSource(); |
| if (Arrays.binarySearch(expectedTokenTypes, this.currentToken) < 0) { |
| StringBuffer expectations = new StringBuffer(5); |
| for (int i = 0; i < expectedTokenTypes.length; i++){ |
| if (i > 0) { |
| expectations.append(','); |
| } |
| expectations.append(expectedTokenTypes[i]); |
| } |
| throw new AbortFormatting("unexpected token type, expecting:["+expectations.toString()+"], actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$ |
| } |
| this.print(currentTokenSource, considerSpaceIfAny); |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| public void printQualifiedReference(int sourceEnd) { |
| int currentTokenStartPosition = this.scanner.currentPosition; |
| try { |
| do { |
| this.printComment(); |
| switch(this.currentToken = this.scanner.getNextToken()) { |
| case TerminalTokens.TokenNameEOF : |
| return; |
| case TerminalTokens.TokenNameWHITESPACE : |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_BLOCK : |
| case TerminalTokens.TokenNameCOMMENT_JAVADOC : |
| this.printBlockComment(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_LINE : |
| this.printCommentLine(this.scanner.getRawTokenSource()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| case TerminalTokens.TokenNameIdentifier : |
| case TerminalTokens.TokenNameDOT : |
| this.print(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| break; |
| default: |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } while (this.scanner.currentPosition <= sourceEnd); |
| } catch(InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| private void printRule(StringBuffer stringBuffer) { |
| for (int i = 0; i < this.pageWidth; i++){ |
| if ((i % this.tabLength) == 0) { |
| stringBuffer.append('+'); |
| } else { |
| stringBuffer.append('-'); |
| } |
| } |
| stringBuffer.append(this.lineSeparator); |
| |
| for (int i = 0; i < (pageWidth / tabLength); i++) { |
| stringBuffer.append(i); |
| stringBuffer.append('\t'); |
| } |
| } |
| |
| public void printTrailingComment() { |
| try { |
| // if we have a space between two tokens we ensure it will be dumped in the formatted string |
| int currentTokenStartPosition = this.scanner.currentPosition; |
| boolean hasWhitespaces = false; |
| boolean hasComment = false; |
| boolean hasLineComment = false; |
| while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { |
| switch(this.currentToken) { |
| case TerminalTokens.TokenNameWHITESPACE : |
| int count = 0; |
| char[] whiteSpaces = this.scanner.getCurrentTokenSource(); |
| for (int i = 0, max = whiteSpaces.length; i < max; i++) { |
| switch(whiteSpaces[i]) { |
| case '\r' : |
| if ((i + 1) < max) { |
| if (whiteSpaces[i + 1] == '\n') { |
| i++; |
| } |
| } |
| count++; |
| break; |
| case '\n' : |
| count++; |
| } |
| } |
| if (hasLineComment) { |
| if (count >= 1) { |
| currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition(); |
| this.preserveEmptyLines(count, currentTokenStartPosition); |
| addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition()); |
| this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1); |
| return; |
| } else { |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } else if (count >= 1) { |
| if (hasComment) { |
| this.printNewLine(this.scanner.getCurrentTokenStartPosition()); |
| } |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } else { |
| hasWhitespaces = true; |
| currentTokenStartPosition = this.scanner.currentPosition; |
| addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition()); |
| } |
| break; |
| case TerminalTokens.TokenNameCOMMENT_LINE : |
| if (hasWhitespaces) { |
| space(); |
| } |
| this.printCommentLine(this.scanner.getRawTokenSource()); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasLineComment = true; |
| break; |
| case TerminalTokens.TokenNameCOMMENT_BLOCK : |
| if (hasWhitespaces) { |
| space(); |
| } |
| this.printBlockComment(this.scanner.getRawTokenSource(), false); |
| currentTokenStartPosition = this.scanner.currentPosition; |
| hasComment = true; |
| break; |
| default : |
| // step back one token |
| this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1); |
| return; |
| } |
| } |
| } catch (InvalidInputException e) { |
| throw new AbortFormatting(e); |
| } |
| } |
| |
| void redoAlignment(AlignmentException e){ |
| if (e.relativeDepth > 0) { // if exception targets a distinct context |
| e.relativeDepth--; // record fact that current context got traversed |
| this.currentAlignment = this.currentAlignment.enclosing; // pop currentLocation |
| throw e; // rethrow |
| } |
| // reset scribe/scanner to restart at this given location |
| this.resetAt(this.currentAlignment.location); |
| this.scanner.resetTo(this.currentAlignment.location.inputOffset, this.scanner.eofPosition); |
| // clean alignment chunkKind so it will think it is a new chunk again |
| this.currentAlignment.chunkKind = 0; |
| } |
| |
| void redoMemberAlignment(AlignmentException e){ |
| // reset scribe/scanner to restart at this given location |
| this.resetAt(this.memberAlignment.location); |
| this.scanner.resetTo(this.memberAlignment.location.inputOffset, this.scanner.eofPosition); |
| // clean alignment chunkKind so it will think it is a new chunk again |
| this.memberAlignment.chunkKind = 0; |
| } |
| |
| public void reset() { |
| this.checkLineWrapping = true; |
| this.line = 0; |
| this.column = 1; |
| this.editsIndex = 0; |
| this.nlsTagCounter = 0; |
| } |
| |
| private void resetAt(Location location) { |
| this.line = location.outputLine; |
| this.column = location.outputColumn; |
| this.indentationLevel = location.outputIndentationLevel; |
| this.numberOfIndentations = location.numberOfIndentations; |
| this.lastNumberOfNewLines = location.lastNumberOfNewLines; |
| this.needSpace = location.needSpace; |
| this.pendingSpace = location.pendingSpace; |
| this.editsIndex = location.editsIndex; |
| this.nlsTagCounter = location.nlsTagCounter; |
| if (this.editsIndex > 0) { |
| this.edits[this.editsIndex - 1] = location.textEdit; |
| } |
| this.formatter.lastLocalDeclarationSourceStart = location.lastLocalDeclarationSourceStart; |
| } |
| |
| private void resize() { |
| System.arraycopy(this.edits, 0, (this.edits = new OptimizedReplaceEdit[this.editsIndex * 2]), 0, this.editsIndex); |
| } |
| |
| public void space() { |
| if (!this.needSpace) return; |
| this.lastNumberOfNewLines = 0; |
| this.pendingSpace = true; |
| this.column++; |
| this.needSpace = false; |
| } |
| |
| public String toString() { |
| StringBuffer stringBuffer = new StringBuffer(); |
| stringBuffer |
| .append("(page width = " + this.pageWidth + ") - (tabChar = ");//$NON-NLS-1$//$NON-NLS-2$ |
| switch(this.tabChar) { |
| case DefaultCodeFormatterOptions.TAB : |
| stringBuffer.append("TAB");//$NON-NLS-1$ |
| break; |
| case DefaultCodeFormatterOptions.SPACE : |
| stringBuffer.append("SPACE");//$NON-NLS-1$ |
| break; |
| default : |
| stringBuffer.append("MIXED");//$NON-NLS-1$ |
| } |
| stringBuffer |
| .append(") - (tabSize = " + this.tabLength + ")")//$NON-NLS-1$//$NON-NLS-2$ |
| .append(this.lineSeparator) |
| .append("(line = " + this.line + ") - (column = " + this.column + ") - (identationLevel = " + this.indentationLevel + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| .append(this.lineSeparator) |
| .append("(needSpace = " + this.needSpace + ") - (lastNumberOfNewLines = " + this.lastNumberOfNewLines + ") - (checkLineWrapping = " + this.checkLineWrapping + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| .append(this.lineSeparator) |
| .append("==================================================================================") //$NON-NLS-1$ |
| .append(this.lineSeparator); |
| printRule(stringBuffer); |
| return stringBuffer.toString(); |
| } |
| |
| public void unIndent() { |
| this.indentationLevel -= this.indentationSize; |
| this.numberOfIndentations--; |
| } |
| } |