/******************************************************************************* * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v0.5 * * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v05.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package net.sourceforge.phpdt.internal.formatter; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.Hashtable; //import java.util.Locale; import java.util.Map; //import javax.swing.text.html.Option; import net.sourceforge.phpdt.core.ICodeFormatter; import net.sourceforge.phpdt.core.compiler.CharOperation; import net.sourceforge.phpdt.core.compiler.ITerminalSymbols; import net.sourceforge.phpdt.core.compiler.InvalidInputException; import net.sourceforge.phpdt.internal.compiler.ConfigurableOption; import net.sourceforge.phpdt.internal.compiler.parser.Scanner; import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions; import net.sourceforge.phpdt.internal.formatter.impl.SplitLine; /** *
CodeFormatter
* void format(aString)
on this instance to
* format aString
. It will return the formatted string.
* constructions
stack.
*/
public static final TokenName BLOCK = TokenName.LBRACE;
/**
* Represents a block following a control statement in the
* constructions
stack.
*/
public static final TokenName NONINDENT_BLOCK = TokenName.NONE_INDENT_BLOCK;
/**
* Contains the formatted output.
*/
StringBuffer formattedSource;
/**
* Contains the current line. constructions
array.
*/
private int constructionsCount;
/**
* Level of indentation of the current token (number of tab char put in
* front of it).
*/
private int indentationLevel;
/**
* Regular level of indentation of all the lines
*/
private int initialIndentationLevel;
/**
* Used to split a line.
*/
Scanner splitScanner;
/**
* To remember the offset between the beginning of the line and the
* beginning of the comment.
*/
int currentCommentOffset;
int currentLineIndentationLevel;
int maxLineSize = 30;
private boolean containsOpenCloseBraces;
private int indentationLevelForOpenCloseBraces;
/**
* Collections of positions to map
*/
private int[] positionsToMap;
/**
* Collections of mapped positions
*/
private int[] mappedPositions;
private int indexToMap;
private int indexInMap;
private int globalDelta;
private int lineDelta;
private int splitDelta;
private int beginningOfLineIndex;
private int multipleLineCommentCounter;
/**
* Creates a new instance of Code Formatter using the given settings.
*
* @deprecated backport 1.0 internal functionality
*/
public CodeFormatter(ConfigurableOption[] settings) {
this(convertConfigurableOptions(settings));
}
/**
* Creates a new instance of Code Formatter using the FormattingOptions
* object given as argument
*
* @deprecated Use CodeFormatter(ConfigurableOption[]) instead
*/
// public CodeFormatter() {
// this((Map) null);
// }
/**
* Creates a new instance of Code Formatter using the given settings.
*/
public CodeFormatter(Map settings) {
// initialize internal state
constructionsCount = 0;
constructions = new TokenName[10];
currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
currentCommentOffset = -1;
// initialize primary and secondary scanners
scanner = new Scanner(true /* comment */
, true /* whitespace */
, false /* nls */
, false /* assert */
, true, /* tokenizeStrings */
null, null, true /* taskCaseSensitive */); // regular scanner for
// forming lines
scanner.recordLineSeparator = true;
scanner.ignorePHPOneLiner = true;
// to remind of the position of the beginning of the line.
splitScanner = new Scanner(true /* comment */
, true /* whitespace */
, false /* nls */
, false /* assert */
, true, /* tokenizeStrings */
null, null, true /* taskCaseSensitive */);
splitScanner.ignorePHPOneLiner = true;
// secondary scanner to split long lines formed by primary scanning
// initialize current line buffer
currentLineBuffer = new StringBuffer();
this.options = new FormatterOptions(settings);
}
/**
* Returns true if a lineSeparator has to be inserted before
* operator
false otherwise.
*/
private static boolean breakLineBeforeOperator(TokenName operator) {
switch (operator) {
case COMMA:
case SEMICOLON:
case EQUAL:
return false;
default:
return true;
}
}
/**
* @deprecated backport 1.0 internal functionality
*/
private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
Hashtable options = new Hashtable(10);
for (int i = 0; i < settings.length; i++) {
if (settings[i].getComponentName().equals(
CodeFormatter.class.getName())) {
String optionName = settings[i].getOptionName();
int valueIndex = settings[i].getCurrentValueIndex();
if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
} else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
options
.put(
"net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
return options;
}
/**
* Returns the end of the source code.
*/
private final String copyRemainingSource() {
char str[] = scanner.source;
int startPosition = scanner.startPosition;
int length = str.length - startPosition;
StringBuffer bufr = new StringBuffer(length);
if (startPosition < str.length) {
bufr.append(str, startPosition, length);
}
return (bufr.toString());
}
/**
* Inserts tabCount
tab character or their equivalent number
* of spaces.
*/
private void dumpTab(int tabCount) {
if (options.indentWithTab) {
for (int j = 0; j < tabCount; j++) {
formattedSource.append('\t');
increaseSplitDelta(1);
}
} else {
for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
}
}
/**
* Dumps currentLineBuffer
into the formatted string.
*/
private void flushBuffer() {
String currentString = currentLineBuffer.toString();
splitDelta = 0;
beginningOfLineIndex = formattedSource.length();
if (containsOpenCloseBraces) {
containsOpenCloseBraces = false;
outputLine(currentString,
false,
indentationLevelForOpenCloseBraces,
TokenName.NONE,
-1, null, 0);
indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
} else {
outputLine(currentString,
false,
currentLineIndentationLevel,
TokenName.NONE,
-1, null, 0);
}
int scannerSourceLength = scanner.source.length;
if ((scannerSourceLength > 2)
&& (scanner.startPosition < scannerSourceLength)) {
if (scanner.source[scannerSourceLength - 1] == '\n'
&& scanner.source[scannerSourceLength - 2] == '\r') {
formattedSource.append(options.lineSeparatorSequence);
increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
} else if (scanner.source[scannerSourceLength - 1] == '\n') {
formattedSource.append(options.lineSeparatorSequence);
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
} else if (scanner.source[scannerSourceLength - 1] == '\r') {
formattedSource.append(options.lineSeparatorSequence);
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
}
}
updateMappedPositions(scanner.startPosition);
}
/**
* Formats the input string.
*/
private void format() {
TokenName token = TokenName.NONE;
TokenName previousToken = TokenName.NONE;
TokenName previousCompilableToken = TokenName.NONE;
int indentationOffset = 0;
int newLinesInWhitespace = 0;
// number of new lines in the previous whitespace token
// (used to leave blank lines before comments)
int pendingNewLines = 0;
boolean expectingOpenBrace = false;
boolean clearNonBlockIndents = false;
// true if all indentations till the 1st { (usefull after } or ;)
boolean pendingSpace = true;
boolean pendingNewlineAfterParen = false;
// true when a cr is to be put after a ) (in conditional statements)
boolean inAssignment = false;
boolean inArrayAssignment = false;
boolean inThrowsClause = false;
boolean inClassOrInterfaceHeader = false;
int dollarBraceCount = 0;
// openBracketCount is used to count the number of open brackets not
// closed
// yet.
int openBracketCount = 0;
int unarySignModifier = 0;
// openParenthesis[0] is used to count the parenthesis not belonging to
// a
// condition
// (eg foo();). parenthesis in for (...) are count elsewhere in the
// array.
int openParenthesisCount = 1;
int[] openParenthesis = new int[10];
// tokenBeforeColon is used to know what token goes along with the
// current
// :
// it can be case or ?
int tokenBeforeColonCount = 0;
TokenName[] tokenBeforeColon = new TokenName[10];
constructionsCount = 0; // initializes the constructions count.
// contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
TokenName nlicsToken = ITerminalSymbols.TokenName.NONE;
// fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and
// else
boolean specialElse = false;
// OPTION (IndentationLevel): initial indentation level may be non-zero.
currentLineIndentationLevel += constructionsCount;
// An InvalidInputException exception might cause the termination of
// this
// loop.
int arrayDeclarationCount = 0;
int[] arrayDeclarationParenthesis = new int[10];
try {
while (true) {
// Get the next token. Catch invalid input and output it
// with minimal formatting, also catch end of input and
// exit the loop.
try {
token = scanner.getNextToken();
if (Scanner.DEBUG) {
int currentEndPosition = scanner
.getCurrentTokenEndPosition();
int currentStartPosition = scanner
.getCurrentTokenStartPosition();
System.out.print(currentStartPosition + ","
+ currentEndPosition + ": ");
System.out.println(scanner.toStringAction(token));
}
// Patch for line comment
// See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
if (token == ITerminalSymbols.TokenName.COMMENT_LINE) {
int length = scanner.currentPosition;
loop: for (int index = length - 1; index >= 0; index--) {
switch (scanner.source[index]) {
case '\r':
case '\n':
scanner.currentPosition--;
break;
default:
break loop;
}
}
}
} catch (InvalidInputException e) {
if (!handleInvalidToken(e)) {
throw e;
}
token = ITerminalSymbols.TokenName.NONE;
}
if (token == Scanner.TokenName.EOF) {
break;
} else if (token == Scanner.TokenName.HEREDOC) {
// no indentation for heredocs and HTML !
outputCurrentTokenWithoutIndent(Scanner.TokenName.HEREDOC, 0);
continue;
} else if (token == Scanner.TokenName.INLINE_HTML) {
// no indentation for heredocs and HTML !
int newLineCount = 1;
if (scanner.startPosition == 0) {
newLineCount = 0;
}
outputCurrentTokenWithoutIndent(
Scanner.TokenName.INLINE_HTML, newLineCount);
int srcLen = scanner.source.length;
if (scanner.currentPosition < srcLen - 1) {
newLine(1);
}
continue;
}
/*
* ## MODIFYING the indentation level before generating new
* lines and indentation in the output string
*/
// Removes all the indentations made by statements not followed
// by a
// block
// except if the current token is ELSE, CATCH or if we are in a
// switch/case
if (clearNonBlockIndents
&& (token != Scanner.TokenName.WHITESPACE)) {
switch (token) {
case ELSE:
if (constructionsCount > 0
&& constructions[constructionsCount - 1] == TokenName.ELSE) {
pendingNewLines = 1;
specialElse = true;
}
indentationLevel += popInclusiveUntil(TokenName.IF);
break;
// case TokenName.catch :
// indentationLevel += popInclusiveUntil(TokenName.catch);
// break;
// case TokenName.finally :
// indentationLevel += popInclusiveUntil(TokenName.catch);
// break;
case WHILE:
if (nlicsToken == TokenName.DO) {
indentationLevel += pop(TokenName.DO);
break;
}
default:
indentationLevel += popExclusiveUntilBlockOrCase();
// clear until a CASE, DEFAULT or BLOCK is encountered.
// Thus, the indentationLevel is correctly cleared
// either
// in a switch/case statement or in any other situation.
}
clearNonBlockIndents = false;
}
// returns to the indentation level created by the SWITCH
// keyword
// if the current token is a CASE or a DEFAULT
if (token == TokenName.CASE || token == TokenName.DEFAULT) {
indentationLevel += pop(TokenName.CASE);
}
// if (token == Scanner.TokenName.throws) {
// inThrowsClause = true;
// }
if ((token == Scanner.TokenName.CLASS || token == Scanner.TokenName.INTERFACE)
&& previousToken != Scanner.TokenName.DOT) {
inClassOrInterfaceHeader = true;
}
/*
* ## APPEND newlines and indentations to the output string
*/
// Do not add a new line between ELSE and IF, if the option
// elseIfOnSameLine is true.
// Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
// if (pendingNewlineAfterParen
// && previousCompilableToken == TokenName.else
// && token == TokenName.if
// && options.compactElseIfMode) {
// pendingNewlineAfterParen = false;
// pendingNewLines = 0;
// indentationLevel += pop(TokenName.else);
// // because else if is now one single statement,
// // the indentation level after it is increased by one and not
// by 2
// // (else = 1 indent, if = 1 indent, but else if = 1 indent,
// not 2).
// }
// Add a newline & indent to the formatted source string if
// a for/if-else/while statement was scanned and there is no
// block
// following it.
pendingNewlineAfterParen = pendingNewlineAfterParen
|| (previousCompilableToken == TokenName.RPAREN && token == TokenName.LBRACE);
if (pendingNewlineAfterParen
&& token != Scanner.TokenName.WHITESPACE) {
pendingNewlineAfterParen = false;
// Do to add a newline & indent sequence if the current
// token is an
// open brace or a period or if the current token is a
// semi-colon and
// the
// previous token is a close paren.
// add a new line if a parenthesis belonging to a for()
// statement
// has been closed and the current token is not an opening
// brace
if (token != TokenName.LBRACE && !isComment(token)
// to avoid adding new line between else and a
// comment
&& token != TokenName.DOT
&& !(previousCompilableToken == TokenName.RPAREN && token == TokenName.SEMICOLON)) {
newLine(1);
currentLineIndentationLevel = indentationLevel;
pendingNewLines = 0;
pendingSpace = false;
} else {
if (token == TokenName.LBRACE
&& options.newLineBeforeOpeningBraceMode) {
newLine(1);
if (constructionsCount > 0
&& constructions[constructionsCount - 1] != BLOCK
&& constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
currentLineIndentationLevel = indentationLevel - 1;
} else {
currentLineIndentationLevel = indentationLevel;
}
pendingNewLines = 0;
pendingSpace = false;
}
}
}
if (token == TokenName.LBRACE
&& options.newLineBeforeOpeningBraceMode
&& constructionsCount > 0
&& constructions[constructionsCount - 1] == TokenName.DO) {
newLine(1);
currentLineIndentationLevel = indentationLevel - 1;
pendingNewLines = 0;
pendingSpace = false;
}
// see PR 1G5G8EC
if (token == TokenName.LBRACE && inThrowsClause) {
inThrowsClause = false;
if (options.newLineBeforeOpeningBraceMode) {
newLine(1);
currentLineIndentationLevel = indentationLevel;
pendingNewLines = 0;
pendingSpace = false;
}
}
// see PR 1G5G82G
if (token == TokenName.LBRACE && inClassOrInterfaceHeader) {
inClassOrInterfaceHeader = false;
if (options.newLineBeforeOpeningBraceMode) {
newLine(1);
currentLineIndentationLevel = indentationLevel;
pendingNewLines = 0;
pendingSpace = false;
}
}
// don't linebreak empty array declarations
if (token == TokenName.RPAREN && arrayDeclarationCount > 0) {
if (previousCompilableToken == TokenName.LPAREN) {
pendingNewLines = 0;
}
}
// Add pending new lines to the formatted source string.
// Note: pending new lines are not added if the current token
// is a single line comment or whitespace.
// if the comment is between parenthesis, there is no blank line
// preservation
// (if it's a one-line comment, a blank line is added after it).
if (((pendingNewLines > 0 && (!isComment(token)))
|| (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
|| (previousCompilableToken == TokenName.LBRACE && token == TokenName.RBRACE) || (newLinesInWhitespace > 0 && previousCompilableToken == TokenName.DOT))
&& token != Scanner.TokenName.WHITESPACE) {
// Do not add newline & indent between an adjoining close
// brace and
// close paren. Anonymous inner classes may use this form.
boolean closeBraceAndCloseParen = previousToken == TokenName.RBRACE
&& token == TokenName.RPAREN;
// OPTION (NewLineInCompoundStatement): do not add newline &
// indent
// between close brace and else, (do) while, catch, and
// finally if
// newlineInCompoundStatement is true.
boolean nlicsOption = previousToken == TokenName.RBRACE
&& !options.newlineInControlStatementMode
&& (token == TokenName.ELSE
|| (token == TokenName.WHILE && nlicsToken == TokenName.DO)
|| token == TokenName.CATCH || token == TokenName.FINALLY);
// Do not add a newline & indent between a close brace and
// semi-colon.
boolean semiColonAndCloseBrace = previousToken == TokenName.RBRACE
&& token == TokenName.SEMICOLON;
// Do not add a new line & indent between a multiline
// comment and a
// opening brace
boolean commentAndOpenBrace = previousToken == Scanner.TokenName.COMMENT_BLOCK
&& token == TokenName.LBRACE;
// Do not add a newline & indent between a close brace and a
// colon
// (in array assignments, for example).
boolean commaAndCloseBrace = previousToken == TokenName.RBRACE
&& token == TokenName.COMMA;
// Add a newline and indent, if appropriate.
if (specialElse
|| (!commentAndOpenBrace
&& !closeBraceAndCloseParen && !nlicsOption
&& !semiColonAndCloseBrace && !commaAndCloseBrace)) {
// if clearAllBlankLinesMode=false, leaves the blank
// lines
// inserted by the user
// if clearAllBlankLinesMode=true, removes all of then
// and insert only blank lines required by the
// formatting.
if (!options.clearAllBlankLinesMode) {
// (isComment(token))
pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace
: pendingNewLines;
pendingNewLines = (pendingNewLines > 2) ? 2
: pendingNewLines;
}
if (previousCompilableToken == TokenName.LBRACE
&& token == TokenName.RBRACE) {
containsOpenCloseBraces = true;
indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
if (isComment(previousToken)) {
newLine(pendingNewLines);
} else {
/*
* if (!(constructionsCount > 1 &&
* constructions[constructionsCount-1] ==
* NONINDENT_BLOCK &&
* (constructions[constructionsCount-2] ==
* TokenName.for
*/
if (options.newLineInEmptyBlockMode) {
if (inArrayAssignment) {
newLine(1); // array assigment with an
// empty block
} else {
newLine(pendingNewLines);
}
}
// }
}
} else {
// see PR 1FKKC3U: LFCOM:WINNT - Format problem with
// a comment
// before the ';'
if (!((previousToken == Scanner.TokenName.COMMENT_BLOCK || previousToken == Scanner.TokenName.COMMENT_PHPDOC) && token == TokenName.SEMICOLON)) {
newLine(pendingNewLines);
}
}
if (((previousCompilableToken == TokenName.SEMICOLON)
|| (previousCompilableToken == TokenName.LBRACE)
|| (previousCompilableToken == TokenName.RBRACE) || (isComment(previousToken)))
&& (token == TokenName.RBRACE)) {
indentationOffset = -1;
indentationLevel += popExclusiveUntilBlock();
}
if (previousToken == Scanner.TokenName.COMMENT_LINE
&& inAssignment) {
// PR 1FI5IPO
currentLineIndentationLevel++;
} else {
currentLineIndentationLevel = indentationLevel
+ indentationOffset;
}
pendingSpace = false;
indentationOffset = 0;
}
pendingNewLines = 0;
newLinesInWhitespace = 0;
specialElse = false;
if (nlicsToken == TokenName.DO && token == TokenName.WHILE) {
nlicsToken = ITerminalSymbols.TokenName.NONE;
}
}
boolean phpTagAndWhitespace = previousToken == TokenName.INLINE_HTML
&& token == TokenName.WHITESPACE;
switch (token) {
// case TokenName.DOLLAR :
// dollarBraceCount++;
// break;
case ELSE:
// case TokenName.finally :
expectingOpenBrace = true;
pendingNewlineAfterParen = true;
indentationLevel += pushControlStatement(token);
break;
case CASE:
case DEFAULT:
if (tokenBeforeColonCount == tokenBeforeColon.length) {
System
.arraycopy(
tokenBeforeColon,
0,
(tokenBeforeColon = new TokenName[tokenBeforeColonCount * 2]),
0, tokenBeforeColonCount);
}
tokenBeforeColon[tokenBeforeColonCount++] = TokenName.CASE;
indentationLevel += pushControlStatement(TokenName.CASE);
break;
case QUESTION:
if (tokenBeforeColonCount == tokenBeforeColon.length) {
System
.arraycopy(
tokenBeforeColon,
0,
(tokenBeforeColon = new TokenName[tokenBeforeColonCount * 2]),
0, tokenBeforeColonCount);
}
tokenBeforeColon[tokenBeforeColonCount++] = token;
break;
case SWITCH:
case FOR:
case FOREACH:
case IF:
case WHILE:
if (openParenthesisCount == openParenthesis.length) {
System
.arraycopy(
openParenthesis,
0,
(openParenthesis = new int[openParenthesisCount * 2]),
0, openParenthesisCount);
}
openParenthesis[openParenthesisCount++] = 0;
expectingOpenBrace = true;
indentationLevel += pushControlStatement(token);
break;
case TRY:
pendingNewlineAfterParen = true;
case CATCH:
// several CATCH statements can be contiguous.
// a CATCH is encountered pop until first CATCH (if a CATCH
// follows a TRY it works the same way,
// as CATCH and TRY are the same token in the stack).
expectingOpenBrace = true;
indentationLevel += pushControlStatement(TokenName.CATCH);
break;
case DO:
expectingOpenBrace = true;
indentationLevel += pushControlStatement(token);
nlicsToken = token;
break;
case NEW:
break;
case LPAREN:
// if (previousToken == TokenName.synchronized) {
// indentationLevel += pushControlStatement(previousToken);
// } else {
// Put a space between the previous and current token if the
// previous token was not a keyword, open paren, logical
// compliment (eg: !), semi-colon, open brace, close brace,
// super, or this.
if (previousCompilableToken != TokenName.LBRACKET
&& previousToken != TokenName.IDENTIFIER
&& previousToken != TokenName.NONE
&& previousToken != TokenName.NOT
&& previousToken != TokenName.LPAREN
&& previousToken != TokenName.TWIDDLE
&& previousToken != TokenName.SEMICOLON
&& previousToken != TokenName.LBRACE
&& previousToken != TokenName.RBRACE
&& previousToken != TokenName.SUPER) {
// && previousToken != TokenName.THIS) {
if (!options.compactArrays) {
space();
}
}
// If in a for/if/while statement, increase the parenthesis
// count
// for the current openParenthesisCount
// else increase the count for stand alone parenthesis.
if (openParenthesisCount > 0)
openParenthesis[openParenthesisCount - 1]++;
else
openParenthesis[0]++;
pendingSpace = false;
// recognize array declaration for nice output
if (previousCompilableToken == TokenName.ARRAY) {
arrayDeclarationCount++;
arrayDeclarationParenthesis[arrayDeclarationCount] = openParenthesis[openParenthesisCount - 1];
if (!options.compactArrays) {
indentationLevel++;
pendingNewLines = 1;
}
}
// S }
break;
case RPAREN:
// check for closing array declaration
if (arrayDeclarationCount > 0) {
if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
if (previousCompilableToken != TokenName.LPAREN) {
if (!options.compactArrays) {
newLine(1);
}
} else if (previousToken == TokenName.COMMENT_LINE
|| previousToken == TokenName.COMMENT_BLOCK
|| previousToken == TokenName.COMMENT_PHPDOC) {
// prevent to combine comment line and statement line (#1475484)
if (!options.compactArrays) {
newLine(1);
}
}
if (!options.compactArrays) {
indentationLevel--;
}
currentLineIndentationLevel = indentationLevel;
pendingNewLines = 0;
arrayDeclarationCount--;
}
}
// Decrease the parenthesis count
// if there is no more unclosed parenthesis,
// a new line and indent may be append (depending on the
// next
// token).
if ((openParenthesisCount > 1)
&& (openParenthesis[openParenthesisCount - 1] > 0)) {
openParenthesis[openParenthesisCount - 1]--;
if (openParenthesis[openParenthesisCount - 1] <= 0) {
pendingNewlineAfterParen = true;
inAssignment = false;
openParenthesisCount--;
}
} else {
openParenthesis[0]--;
}
pendingSpace = false;
break;
case LBRACE:
if (previousCompilableToken == TokenName.DOLLAR) {
dollarBraceCount++;
} else {
if ((previousCompilableToken == TokenName.RBRACKET)
|| (previousCompilableToken == TokenName.EQUAL)) {
// if (previousCompilableToken == TokenName.RBRACKET)
// {
inArrayAssignment = true;
inAssignment = false;
}
if (inArrayAssignment) {
indentationLevel += pushBlock();
} else {
// Add new line and increase indentation level after
// open brace.
pendingNewLines = 1;
indentationLevel += pushBlock();
inAssignment = false;
}
}
break;
case RBRACE:
if (dollarBraceCount > 0) {
dollarBraceCount--;
break;
}
if (previousCompilableToken == TokenName.RPAREN) {
pendingSpace = false;
}
if (inArrayAssignment) {
inArrayAssignment = false;
pendingNewLines = 1;
indentationLevel += popInclusiveUntilBlock();
} else {
pendingNewLines = 1;
indentationLevel += popInclusiveUntilBlock();
if (previousCompilableToken == TokenName.RPAREN) {
// fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on
// message
// expression
currentLineBuffer
.append(options.lineSeparatorSequence);
increaseLineDelta(options.lineSeparatorSequence.length);
}
if (constructionsCount > 0) {
switch (constructions[constructionsCount - 1]) {
case FOR:
case FOREACH:
// indentationLevel += popExclusiveUntilBlock();
// break;
case SWITCH:
case IF:
case ELSE:
case TRY:
case CATCH:
case FINALLY:
case WHILE:
case DO:
// case TokenName.synchronized :
clearNonBlockIndents = true;
default:
break;
}
}
}
break;
case LBRACKET:
openBracketCount++;
pendingSpace = false;
break;
case RBRACKET:
openBracketCount -= (openBracketCount > 0) ? 1 : 0;
// if there is no left bracket to close, the right bracket
// is
// ignored.
pendingSpace = false;
break;
case COMMA:
pendingSpace = false;
if (arrayDeclarationCount > 0) {
if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
// there is no left parenthesis to close in current array declaration (#1475484)
if (!options.compactArrays) {
pendingNewLines = 1;
}
}
}
break;
case DOT:
if (!options.compactStringConcatenation) {
space();
}
pendingSpace = false;
break;
case SEMICOLON:
// Do not generate line terminators in the definition of
// the for statement.
// if not in this case, jump a line and reduce indentation
// after
// the brace
// if the block it closes belongs to a conditional statement
// (if,
// while, do...).
if (openParenthesisCount <= 1) {
pendingNewLines = 1;
if (expectingOpenBrace) {
clearNonBlockIndents = true;
expectingOpenBrace = false;
}
}
inAssignment = false;
pendingSpace = false;
break;
case PLUS_PLUS:
case MINUS_MINUS:
// Do not put a space between a post-increment/decrement
// and the identifier being modified.
if (previousToken == TokenName.IDENTIFIER
|| previousToken == TokenName.RBRACKET
|| previousToken == TokenName.VARIABLE) {
pendingSpace = false;
}
break;
case PLUS:
// previously ADDITION
case MINUS:
// Handle the unary operators plus and minus via a flag
if (!isLiteralToken(previousToken)
&& previousToken != TokenName.IDENTIFIER
&& previousToken != TokenName.RPAREN
&& previousToken != TokenName.RBRACKET) {
unarySignModifier = 1;
}
break;
case COLON:
// In a switch/case statement, add a newline & indent
// when a colon is encountered.
if (tokenBeforeColonCount > 0) {
if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenName.CASE) {
pendingNewLines = 1;
}
tokenBeforeColonCount--;
}
break;
case EQUAL:
inAssignment = true;
break;
case COMMENT_LINE:
pendingNewLines = 1;
//if (inAssignment) {
// currentLineIndentationLevel++;
//}
break; // a line is always inserted after a one-line
// comment
case COMMENT_PHPDOC:
case COMMENT_BLOCK:
currentCommentOffset = getCurrentCommentOffset();
pendingNewLines = 1;
break;
case WHITESPACE:
if (!phpTagAndWhitespace) {
// Count the number of line terminators in the
// whitespace so
// line spacing can be preserved near comments.
char[] source = scanner.source;
newLinesInWhitespace = 0;
for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
if (source[i] == '\r') {
if (i < max - 1) {
if (source[++i] == '\n') {
newLinesInWhitespace++;
} else {
newLinesInWhitespace++;
}
} else {
newLinesInWhitespace++;
}
} else if (source[i] == '\n') {
newLinesInWhitespace++;
}
}
increaseLineDelta(scanner.startPosition
- scanner.currentPosition);
break;
}
// case TokenName.HTML :
// // Add the next token to the formatted source string.
// // outputCurrentToken(token);
// int startPosition = scanner.startPosition;
// flushBuffer();
// for (int i = startPosition, max =
// scanner.currentPosition; i <
// max; i++) {
// char currentCharacter = scanner.source[i];
// updateMappedPositions(i);
// currentLineBuffer.append(currentCharacter);
// }
// break;
default:
if ((token == TokenName.IDENTIFIER) || isLiteralToken(token)
|| token == TokenName.SUPER) {
// || token == TokenName.this) {
// Do not put a space between a unary operator
// (eg: ++, --, +, -) and the identifier being modified.
if (previousToken == TokenName.PLUS_PLUS
|| previousToken == TokenName.MINUS_MINUS
|| (previousToken == TokenName.MINUS_GREATER && options.compactDereferencingMode) // ->
|| (previousToken == TokenName.PLUS && unarySignModifier > 0)
|| (previousToken == TokenName.MINUS && unarySignModifier > 0)) {
pendingSpace = false;
}
unarySignModifier = 0;
}
break;
}
// Do not output whitespace tokens.
if (token != Scanner.TokenName.WHITESPACE || phpTagAndWhitespace) {
/*
* Add pending space to the formatted source string. Do not
* output a space under the following circumstances: 1) this
* is the first pass 2) previous token is an open paren 3)
* previous token is a period 4) previous token is the
* logical compliment (eg: !) 5) previous token is the
* bitwise compliment (eg: ~) 6) previous token is the open
* bracket (eg: [) 7) in an assignment statement, if the
* previous token is an open brace or the current token is a
* close brace 8) previous token is a single line comment 9)
* current token is a '->'
*/
if (token == TokenName.MINUS_GREATER
&& options.compactDereferencingMode)
pendingSpace = false;
boolean openAndCloseBrace = previousCompilableToken == TokenName.LBRACE
&& token == TokenName.RBRACE;
if (pendingSpace
&& insertSpaceAfter(previousToken)
&& !(inAssignment && (previousToken == TokenName.LBRACE || token == TokenName.RBRACE))
&& previousToken != Scanner.TokenName.COMMENT_LINE) {
if ((!(options.compactAssignmentMode && token == TokenName.EQUAL))
&& !openAndCloseBrace)
space();
}
// Add the next token to the formatted source string.
outputCurrentToken(token);
if (token == Scanner.TokenName.COMMENT_LINE
&& openParenthesisCount > 1) {
pendingNewLines = 0;
currentLineBuffer.append(options.lineSeparatorSequence);
increaseLineDelta(options.lineSeparatorSequence.length);
}
pendingSpace = true;
}
// Whitespace tokens do not need to be remembered.
if (token != Scanner.TokenName.WHITESPACE || phpTagAndWhitespace) {
previousToken = token;
if (token != Scanner.TokenName.COMMENT_BLOCK
&& token != Scanner.TokenName.COMMENT_LINE
&& token != Scanner.TokenName.COMMENT_PHPDOC) {
previousCompilableToken = token;
}
}
}
output(copyRemainingSource());
flushBuffer();
// dump the last token of the source in the formatted output.
} catch (InvalidInputException e) {
output(copyRemainingSource());
flushBuffer();
// dump the last token of the source in the formatted output.
}
}
/**
* Formats the char array sourceString
, and returns a string
* containing the formatted version.
*
* @return the formatted ouput.
*/
public String formatSourceString(String sourceString) {
char[] sourceChars = sourceString.toCharArray();
formattedSource = new StringBuffer(sourceChars.length);
scanner.setSource(sourceChars);
format();
return formattedSource.toString();
}
/**
* Formats the char array sourceString
, and returns a string
* containing the formatted version.
*
* @param string
* the string to format
* @param indentationLevel
* the initial indentation level
* @return the formatted ouput.
*/
public String format(String string, int indentationLevel) {
return format(string, indentationLevel, (int[]) null);
}
/**
* Formats the char array sourceString
, and returns a string
* containing the formatted version. The positions array is modified to
* contain the mapped positions.
*
* @param string
* the string to format
* @param indentationLevel
* the initial indentation level
* @param positions
* the array of positions to map
* @return the formatted ouput.
*/
public String format(String string, int indentationLevel, int[] positions) {
return this.format(string, indentationLevel, positions, null);
}
public String format(String string, int indentationLevel, int[] positions,
String lineSeparator) {
if (lineSeparator != null) {
this.options.setLineSeparator(lineSeparator);
}
if (positions != null) {
this.setPositionsToMap(positions);
this.setInitialIndentationLevel(indentationLevel);
String formattedString = this.formatSourceString(string);
int[] mappedPositions = this.getMappedPositions();
System
.arraycopy(mappedPositions, 0, positions, 0,
positions.length);
return formattedString;
} else {
this.setInitialIndentationLevel(indentationLevel);
return this.formatSourceString(string);
}
}
/**
* Formats the char array sourceString
, and returns a string
* containing the formatted version. The initial indentation level is 0.
*
* @param string
* the string to format
* @return the formatted ouput.
*/
public String format(String string) {
return this.format(string, 0, (int[]) null);
}
/**
* Formats a given source string, starting indenting it at a particular
* depth and using the given options
*
* @deprecated backport 1.0 internal functionality
*/
public static String format(String sourceString,
int initialIndentationLevel, ConfigurableOption[] options) {
CodeFormatter formatter = new CodeFormatter(options);
formatter.setInitialIndentationLevel(initialIndentationLevel);
return formatter.formatSourceString(sourceString);
}
/**
* Returns the number of characters and tab char between the beginning of
* the line and the beginning of the comment.
*/
private int getCurrentCommentOffset() {
int linePtr = scanner.linePtr;
// if there is no beginning of line, return 0.
if (linePtr < 0)
return 0;
int offset = 0;
int beginningOfLine = scanner.lineEnds[linePtr];
int currentStartPosition = scanner.startPosition;
char[] source = scanner.source;
// find the position of the beginning of the line containing the comment
while (beginningOfLine > currentStartPosition) {
if (linePtr > 0) {
beginningOfLine = scanner.lineEnds[--linePtr];
} else {
beginningOfLine = 0;
break;
}
}
for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
char currentCharacter = source[i];
switch (currentCharacter) {
case '\t':
offset += options.tabSize;
break;
case ' ':
offset++;
break;
case '\r':
case '\n':
break;
default:
return offset;
}
}
return offset;
}
/**
* Returns an array of descriptions for the configurable options. The
* descriptions may be changed and passed back to a different compiler.
*
* @deprecated backport 1.0 internal functionality
*/
// public static ConfigurableOption[] getDefaultOptions(Locale locale) {
// String componentName = CodeFormatter.class.getName();
// FormatterOptions options = new FormatterOptions();
// return new ConfigurableOption[] {
// new ConfigurableOption(componentName, "newline.openingBrace",
// locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
// //$NON-NLS-1$
// new ConfigurableOption(componentName,
// "newline.controlStatement", locale,
// options.newlineInControlStatementMode ? 0 : 1),
// //$NON-NLS-1$
// new ConfigurableOption(componentName, "newline.clearAll",
// locale, options.clearAllBlankLinesMode ? 0 : 1),
// //$NON-NLS-1$
// // new ConfigurableOption(componentName, "newline.elseIf",
// // locale,
// // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
// new ConfigurableOption(componentName, "newline.emptyBlock",
// locale, options.newLineInEmptyBlockMode ? 0 : 1),
// //$NON-NLS-1$
// new ConfigurableOption(componentName, "line.split", locale,
// options.maxLineLength),
// //$NON-NLS-1$
// new ConfigurableOption(componentName,
// "style.compactAssignment", locale,
// options.compactAssignmentMode ? 0 : 1),
// //$NON-NLS-1$
// new ConfigurableOption(componentName, "tabulation.char",
// locale, options.indentWithTab ? 0 : 1),
// //$NON-NLS-1$
// new ConfigurableOption(componentName,
// "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
// };
// }
/**
* Returns the array of mapped positions. Returns null is no positions have
* been set.
*
* @return int[]
* @deprecated There is no need to retrieve the mapped positions anymore.
*/
public int[] getMappedPositions() {
if (null != mappedPositions) {
for (int i = 0; i < mappedPositions.length; i++) {
if (mappedPositions[i] >= formattedSource.length()) {
mappedPositions[i] = formattedSource.length() - 1;
}
}
}
return mappedPositions;
}
/**
* Returns the priority of the token given as argument token
* @param token
* the token of which the priority is requested
*/
// private static int getTokenPriority(int token) {
// switch (token) {
// case TokenName.extends:
// // case TokenName.implements :
// // case TokenName.throws :
// return 10;
// case TokenName.SEMICOLON:
// // ;
// return 20;
// case TokenName.COMMA:
// // ,
// return 25;
// case TokenName.EQUAL:
// // =
// return 30;
// case TokenName.AND_AND:
// // &&
// case TokenName.OR_OR:
// // ||
// return 40;
// case TokenName.QUESTION:
// // ?
// case TokenName.COLON:
// // :
// return 50; // it's better cutting on ?: than on ;
// case TokenName.EQUAL_EQUAL:
// // ==
// case TokenName.EQUAL_EQUAL_EQUAL:
// // ===
// case TokenName.NOT_EQUAL:
// // !=
// case TokenName.NOT_EQUAL_EQUAL:
// // !=
// return 60;
// case TokenName.LESS:
// // <
// case TokenName.LESS_EQUAL:
// // <=
// case TokenName.GREATER:
// // >
// case TokenName.GREATER_EQUAL:
// // >=
// // case TokenName.instanceof : // instanceof
// return 70;
// case TokenName.PLUS:
// // +
// case TokenName.MINUS:
// // -
// return 80;
// case TokenName.MULTIPLY:
// // *
// case TokenName.DIVIDE:
// // /
// case TokenName.REMAINDER:
// // %
// return 90;
// case TokenName.LEFT_SHIFT:
// // <<
// case TokenName.RIGHT_SHIFT:
// // >>
// // case TokenName.UNSIGNED_RIGHT_SHIFT : // >>>
// return 100;
// case TokenName.AND:
// // &
// case TokenName.OR:
// // |
// case TokenName.XOR:
// // ^
// return 110;
// case TokenName.MULTIPLY_EQUAL:
// // *=
// case TokenName.DIVIDE_EQUAL:
// // /=
// case TokenName.REMAINDER_EQUAL:
// // %=
// case TokenName.PLUS_EQUAL:
// // +=
// case TokenName.MINUS_EQUAL:
// // -=
// case TokenName.LEFT_SHIFT_EQUAL:
// // <<=
// case TokenName.RIGHT_SHIFT_EQUAL:
// // >>=
// // case TokenName.UNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
// case TokenName.AND_EQUAL:
// // &=
// case TokenName.XOR_EQUAL:
// // ^=
// case TokenName.OR_EQUAL:
// // .=
// case TokenName.DOT_EQUAL:
// // |=
// return 120;
// case TokenName.DOT:
// // .
// return 130;
// default:
// return Integer.MAX_VALUE;
// }
// }
/**
* Handles the exception raised when an invalid token is encountered.
* Returns true if the exception has been handled, false otherwise.
*/
private boolean handleInvalidToken(Exception e) {
if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
|| e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
|| e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
return true;
}
return false;
}
private final void increaseGlobalDelta(int offset) {
globalDelta += offset;
}
private final void increaseLineDelta(int offset) {
lineDelta += offset;
}
private final void increaseSplitDelta(int offset) {
splitDelta += offset;
}
/**
* Returns true if a space has to be inserted after operator
* false otherwise.
*/
private boolean insertSpaceAfter(TokenName token) {
switch (token) {
case LPAREN:
case NOT:
case TWIDDLE:
case NONE:
// no token
case WHITESPACE:
case LBRACKET:
case DOLLAR:
case COMMENT_LINE:
return false;
case DOT:
return !options.compactStringConcatenation;
default:
return true;
}
}
/**
* Returns true if a space has to be inserted before operator
* false otherwise. oneLineBuffer
exceeds
* maxLineLength
, it is split and the result is dumped in
* formattedSource
*
* @param newLineCount
* the number of new lines to append
*/
private void newLine(int newLineCount) {
// format current line
splitDelta = 0;
beginningOfLineIndex = formattedSource.length();
String currentLine = currentLineBuffer.toString();
if (containsOpenCloseBraces) {
containsOpenCloseBraces = false;
outputLine(currentLine, false, indentationLevelForOpenCloseBraces,
TokenName.NONE, -1, null, 0);
indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
} else {
outputLine(currentLine, false, currentLineIndentationLevel, TokenName.NONE, -1,
null, 0);
}
// dump line break(s)
for (int i = 0; i < newLineCount; i++) {
formattedSource.append(options.lineSeparatorSequence);
increaseSplitDelta(options.lineSeparatorSequence.length);
}
// reset formatter for next line
int currentLength = currentLine.length();
currentLineBuffer = new StringBuffer(
currentLength > maxLineSize ? maxLineSize = currentLength
: maxLineSize);
increaseGlobalDelta(splitDelta);
increaseGlobalDelta(lineDelta);
lineDelta = 0;
currentLineIndentationLevel = initialIndentationLevel;
}
private String operatorString(TokenName operator) {
switch (operator) {
case EXTENDS:
return "extends"; //$NON-NLS-1$
// case TokenName.implements :
// return "implements"; //$NON-NLS-1$
//
// case TokenName.throws :
// return "throws"; //$NON-NLS-1$
case SEMICOLON:
// ;
return ";"; //$NON-NLS-1$
case COMMA:
// ,
return ","; //$NON-NLS-1$
case EQUAL:
// =
return "="; //$NON-NLS-1$
case AND_AND:
// && (15.22)
return "&&"; //$NON-NLS-1$
case OR_OR:
// || (15.23)
return "||"; //$NON-NLS-1$
case QUESTION:
// ? (15.24)
return "?"; //$NON-NLS-1$
case COLON:
// : (15.24)
return ":"; //$NON-NLS-1$
case PAAMAYIM_NEKUDOTAYIM:
// : (15.24)
return "::"; //$NON-NLS-1$
case EQUAL_EQUAL:
// == (15.20, 15.20.1, 15.20.2, 15.20.3)
return "=="; //$NON-NLS-1$
case EQUAL_EQUAL_EQUAL:
// == (15.20, 15.20.1, 15.20.2, 15.20.3)
return "==="; //$NON-NLS-1$
case EQUAL_GREATER:
// -= (15.25.2)
return "=>"; //$NON-NLS-1$
case NOT_EQUAL:
// != (15.20, 15.20.1, 15.20.2, 15.20.3)
return "!="; //$NON-NLS-1$
case NOT_EQUAL_EQUAL:
// != (15.20, 15.20.1, 15.20.2, 15.20.3)
return "!=="; //$NON-NLS-1$
case LESS:
// < (15.19.1)
return "<"; //$NON-NLS-1$
case LESS_EQUAL:
// <= (15.19.1)
return "<="; //$NON-NLS-1$
case GREATER:
// > (15.19.1)
return ">"; //$NON-NLS-1$
case GREATER_EQUAL:
// >= (15.19.1)
return ">="; //$NON-NLS-1$
// case instanceof : // instanceof
// return "instanceof"; //$NON-NLS-1$
case PLUS:
// + (15.17, 15.17.2)
return "+"; //$NON-NLS-1$
case MINUS:
// - (15.17.2)
return "-"; //$NON-NLS-1$
case MULTIPLY:
// * (15.16.1)
return "*"; //$NON-NLS-1$
case DIVIDE:
// / (15.16.2)
return "/"; //$NON-NLS-1$
case REMAINDER:
// % (15.16.3)
return "%"; //$NON-NLS-1$
case LEFT_SHIFT:
// << (15.18)
return "<<"; //$NON-NLS-1$
case RIGHT_SHIFT:
// >> (15.18)
return ">>"; //$NON-NLS-1$
// case UNSIGNED_RIGHT_SHIFT : // >>> (15.18)
// return ">>>"; //$NON-NLS-1$
case OP_AND:
// & (15.21, 15.21.1, 15.21.2)
return "&"; //$NON-NLS-1$
case OP_OR:
// | (15.21, 15.21.1, 15.21.2)
return "|"; //$NON-NLS-1$
case OP_XOR:
// ^ (15.21, 15.21.1, 15.21.2)
return "^"; //$NON-NLS-1$
case MULTIPLY_EQUAL:
// *= (15.25.2)
return "*="; //$NON-NLS-1$
case DIVIDE_EQUAL:
// /= (15.25.2)
return "/="; //$NON-NLS-1$
case REMAINDER_EQUAL:
// %= (15.25.2)
return "%="; //$NON-NLS-1$
case PLUS_EQUAL:
// += (15.25.2)
return "+="; //$NON-NLS-1$
case MINUS_EQUAL:
// -= (15.25.2)
return "-="; //$NON-NLS-1$
case MINUS_GREATER:
// -= (15.25.2)
return "->"; //$NON-NLS-1$
case LEFT_SHIFT_EQUAL:
// <<= (15.25.2)
return "<<="; //$NON-NLS-1$
case RIGHT_SHIFT_EQUAL:
// >>= (15.25.2)
return ">>="; //$NON-NLS-1$
// case UNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
// return ">>>="; //$NON-NLS-1$
case AND_EQUAL:
// &= (15.25.2)
return "&="; //$NON-NLS-1$
case XOR_EQUAL:
// ^= (15.25.2)
return "^="; //$NON-NLS-1$
case OR_EQUAL:
// |= (15.25.2)
return "|="; //$NON-NLS-1$
case DOT_EQUAL:
// .=
return ".="; //$NON-NLS-1$
case DOT:
// .
return "."; //$NON-NLS-1$
default:
return ""; //$NON-NLS-1$
}
}
/**
* Appends stringToOutput
to the formatted output. token
to the formatted output. \n
, append a LINE_SEPARATOR and indent
* after it.
*/
private void outputCurrentToken(TokenName token) {
char[] source = scanner.source;
int startPosition = scanner.startPosition;
switch (token) {
case COMMENT_PHPDOC:
case COMMENT_BLOCK:
case COMMENT_LINE:
boolean endOfLine = false;
int currentCommentOffset = getCurrentCommentOffset();
int beginningOfLineSpaces = 0;
endOfLine = false;
currentCommentOffset = getCurrentCommentOffset();
beginningOfLineSpaces = 0;
boolean pendingCarriageReturn = false;
for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
char currentCharacter = source[i];
updateMappedPositions(i);
switch (currentCharacter) {
case '\r':
pendingCarriageReturn = true;
endOfLine = true;
break;
case '\n':
if (pendingCarriageReturn) {
increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
} else {
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
}
pendingCarriageReturn = false;
currentLineBuffer.append(options.lineSeparatorSequence);
beginningOfLineSpaces = 0;
endOfLine = true;
break;
case '\t':
if (pendingCarriageReturn) {
pendingCarriageReturn = false;
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
currentLineBuffer.append(options.lineSeparatorSequence);
beginningOfLineSpaces = 0;
endOfLine = true;
}
if (endOfLine) {
// we remove a maximum of currentCommentOffset
// characters (tabs
// are converted to space numbers).
beginningOfLineSpaces += options.tabSize;
if (beginningOfLineSpaces > currentCommentOffset) {
currentLineBuffer.append(currentCharacter);
} else {
increaseGlobalDelta(-1);
}
} else {
currentLineBuffer.append(currentCharacter);
}
break;
case ' ':
if (pendingCarriageReturn) {
pendingCarriageReturn = false;
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
currentLineBuffer.append(options.lineSeparatorSequence);
beginningOfLineSpaces = 0;
endOfLine = true;
}
if (endOfLine) {
// we remove a maximum of currentCommentOffset
// characters (tabs
// are converted to space numbers).
beginningOfLineSpaces++;
if (beginningOfLineSpaces > currentCommentOffset) {
currentLineBuffer.append(currentCharacter);
} else {
increaseGlobalDelta(-1);
}
} else {
currentLineBuffer.append(currentCharacter);
}
break;
default:
if (pendingCarriageReturn) {
pendingCarriageReturn = false;
increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
currentLineBuffer.append(options.lineSeparatorSequence);
beginningOfLineSpaces = 0;
endOfLine = true;
} else {
beginningOfLineSpaces = 0;
currentLineBuffer.append(currentCharacter);
endOfLine = false;
}
}
}
updateMappedPositions(scanner.currentPosition - 1);
multipleLineCommentCounter++;
break;
default:
for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
char currentCharacter = source[i];
updateMappedPositions(i);
currentLineBuffer.append(currentCharacter);
}
}
}
/**
* Outputs currentString
:currentString
* @param operator
* value of the operator belonging to currentString
.
*/
private void outputLine(String currentString, boolean preIndented,
int depth, TokenName operator, int substringIndex,
int[] startSubstringIndexes, int offsetInGlobalLine) {
boolean emptyFirstSubString = false;
String operatorString = operatorString(operator);
boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
boolean placeOperatorAhead = !placeOperatorBehind;
// dump prefix operator?
if (placeOperatorAhead) {
if (!preIndented) {
dumpTab(depth);
preIndented = true;
}
if (operator.compareTo (TokenName.NONE) > 0) {
if (insertSpaceBefore(operator)) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
formattedSource.append(operatorString);
increaseSplitDelta(operatorString.length());
if (insertSpaceAfter(operator)
&& operator != TokenName.IMPLEMENTS
&& operator != TokenName.EXTENDS) {
// && operator != TokenName.throws) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
}
}
SplitLine splitLine = null;
if (options.maxLineLength == 0
|| getLength(currentString, depth) < options.maxLineLength
|| (splitLine = split(currentString, offsetInGlobalLine)) == null) {
// depending on the type of operator, outputs new line before of
// after
// dumping it
// indent before postfix operator
// indent also when the line cannot be split
if (operator == TokenName.EXTENDS || operator == TokenName.IMPLEMENTS) {
// || operator == TokenName.throws) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
if (placeOperatorBehind) {
if (!preIndented) {
dumpTab(depth);
}
}
int max = currentString.length();
if (multipleLineCommentCounter != 0) {
try {
BufferedReader reader = new BufferedReader(
new StringReader(currentString));
String line = reader.readLine();
while (line != null) {
updateMappedPositionsWhileSplitting(
beginningOfLineIndex, beginningOfLineIndex
+ line.length()
+ options.lineSeparatorSequence.length);
formattedSource.append(line);
beginningOfLineIndex = beginningOfLineIndex
+ line.length();
if ((line = reader.readLine()) != null) {
formattedSource
.append(options.lineSeparatorSequence);
beginningOfLineIndex += options.lineSeparatorSequence.length;
dumpTab(currentLineIndentationLevel);
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
updateMappedPositionsWhileSplitting(beginningOfLineIndex,
beginningOfLineIndex + max);
for (int i = 0; i < max; i++) {
char currentChar = currentString.charAt(i);
switch (currentChar) {
case '\r':
break;
case '\n':
if (i != max - 1) {
// fix for 1FFYL5C: LFCOM:ALL - Incorrect
// indentation when
// split with a comment inside a condition
// a substring cannot end with a
// lineSeparatorSequence,
// except if it has been added by format() after a
// one-line
// comment
formattedSource
.append(options.lineSeparatorSequence);
// 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
// expression
dumpTab(depth - 1);
}
break;
default:
formattedSource.append(currentChar);
}
}
}
// update positions inside the mappedPositions table
if (substringIndex != -1) {
if (multipleLineCommentCounter == 0) {
int startPosition = beginningOfLineIndex
+ startSubstringIndexes[substringIndex];
updateMappedPositionsWhileSplitting(startPosition,
startPosition + max);
}
// compute the splitDelta resulting with the operator and blank
// removal
if (substringIndex + 1 != startSubstringIndexes.length) {
increaseSplitDelta(startSubstringIndexes[substringIndex]
+ max - startSubstringIndexes[substringIndex + 1]);
}
}
// dump postfix operator?
if (placeOperatorBehind) {
if (insertSpaceBefore(operator)) {
formattedSource.append(' ');
if (operator.compareTo (TokenName.NONE) > 0) {
increaseSplitDelta(1);
}
}
formattedSource.append(operatorString);
if (operator.compareTo (TokenName.NONE) > 0) {
increaseSplitDelta(operatorString.length());
}
}
return;
}
// fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
// extends has to stand alone on a line when currentString has been
// split.
if (options.maxLineLength != 0 && splitLine != null
&& (operator == TokenName.EXTENDS)) {
// || operator == TokenName.IMPLEMENTS
// || operator == TokenName.THROWS)) {
formattedSource.append(options.lineSeparatorSequence);
increaseSplitDelta(options.lineSeparatorSequence.length);
dumpTab(depth + 1);
} else {
if (operator == TokenName.EXTENDS) {
// || operator == TokenName.implements
// || operator == TokenName.throws) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
}
// perform actual splitting
String result[] = splitLine.substrings;
TokenName[] splitOperators = splitLine.operators;
if (result[0].length() == 0) {
// when the substring 0 is null, the substring 1 is correctly
// indented.
depth--;
emptyFirstSubString = true;
}
// the operator going in front of the result[0] string is the operator
// parameter
for (int i = 0, max = result.length; i < max; i++) {
// the new depth is the current one if this is the first substring,
// the current one + 1 otherwise.
// if the substring is a comment, use the current indentation Level
// instead of the depth
// (-1 because the ouputline increases depth).
// (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of
// line
// comments)
String currentResult = result[i];
if (currentResult.length() != 0 || splitOperators[i].compareTo (TokenName.NONE) > 0) {
int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
|| currentResult.startsWith("//")) //$NON-NLS-1$
? indentationLevel - 1
: depth;
outputLine(currentResult,
i == 0 || (i == 1 && emptyFirstSubString) ? preIndented: false,
i == 0 ? newDepth : newDepth + 1,
splitOperators[i],
i,
splitLine.startSubstringsIndexes,
currentString.indexOf(currentResult));
if (i != max - 1) {
formattedSource.append(options.lineSeparatorSequence);
increaseSplitDelta(options.lineSeparatorSequence.length);
}
}
}
if (result.length == splitOperators.length - 1) {
TokenName lastOperator = splitOperators[result.length];
String lastOperatorString = operatorString(lastOperator);
formattedSource.append(options.lineSeparatorSequence);
increaseSplitDelta(options.lineSeparatorSequence.length);
if (breakLineBeforeOperator(lastOperator)) {
dumpTab(depth + 1);
if (lastOperator.compareTo (TokenName.NONE) > 0) {
if (insertSpaceBefore(lastOperator)) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
formattedSource.append(lastOperatorString);
increaseSplitDelta(lastOperatorString.length());
if (insertSpaceAfter(lastOperator)
&& lastOperator != TokenName.IMPLEMENTS
&& lastOperator != TokenName.EXTENDS) {
// && lastOperator != TokenName.throws) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
}
}
}
if (placeOperatorBehind) {
if (insertSpaceBefore(operator)) {
formattedSource.append(' ');
increaseSplitDelta(1);
}
formattedSource.append(operatorString);
// increaseSplitDelta(operatorString.length());
}
}
/**
* Pops the top statement of the stack if it is token
*/
private int pop(TokenName do1) {
int delta = 0;
if ((constructionsCount > 0)
&& (constructions[constructionsCount - 1] == do1)) {
delta--;
constructionsCount--;
}
return delta;
}
/**
* Pops the top statement of the stack if it is a BLOCK
or a
* NONINDENT_BLOCK
.
*/
// private int popBlock() {
// int delta = 0;
// if ((constructionsCount > 0)
// && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
// if (constructions[constructionsCount - 1] == BLOCK)
// delta--;
// constructionsCount--;
// }
// return delta;
// }
/**
* Pops elements until the stack is empty or the top element is
* token
.token
from the stack.
*
* @param token
* the token to be left as the top of the stack
*/
// private int popExclusiveUntil(int token) {
// int delta = 0;
// int startCount = constructionsCount;
// for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
// if (constructions[i] != NONINDENT_BLOCK)
// delta--;
// constructionsCount--;
// }
// return delta;
// }
/**
* Pops elements until the stack is empty or the top element is a
* BLOCK
or a NONINDENT_BLOCK
.BLOCK
, a NONINDENT_BLOCK
or a
* CASE
.token
.token
from the stack too.
*
* @param token
* the token to remove from the stack
*/
private int popInclusiveUntil(TokenName token) {
int startCount = constructionsCount;
int delta = 0;
for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
if (constructions[i] != NONINDENT_BLOCK)
delta--;
constructionsCount--;
}
if (constructionsCount > 0) {
if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
delta--;
constructionsCount--;
}
return delta;
}
/**
* Pops elements until the stack is empty or the top element is a
* BLOCK
or a NONINDENT_BLOCK
.BLOCK
if the stack is empty or if the top element
* is a BLOCK
, pushes NONINDENT_BLOCK
* otherwise. Creates a new bigger array if the current one is full.
*/
private int pushBlock() {
int delta = 0;
if (constructionsCount == constructions.length)
System.arraycopy(constructions, 0,
(constructions = new TokenName[constructionsCount * 2]), 0,
constructionsCount);
if ((constructionsCount == 0)
|| (constructions[constructionsCount - 1] == BLOCK)
|| (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
|| (constructions[constructionsCount - 1] == TokenName.CASE)) {
delta++;
constructions[constructionsCount++] = BLOCK;
} else {
constructions[constructionsCount++] = NONINDENT_BLOCK;
}
return delta;
}
/**
* Pushes token
.stringToSplit
on the top level token stringToSplit
on the top level token