/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* jAstyle library includes in most of its parts translated C++ code originally
* developed by Jim Pattee and Tal Davidson for the Artistic Style project.
*
* Copyright (C) 2009 by Hector Suarez Barenca http://barenca.net
* Copyright (C) 2013 by Abrar Syed <sacabrarsyed@gmail.com>
* Copyright (C) 2006-2008 by Jim Pattee <jimp03@email.com>
* Copyright (C) 1998-2002 by Tal Davidson
* <http://www.gnu.org/licenses/lgpl-3.0.html>
*
* This file is a part of jAstyle library - an indentation and
* reformatting library for C, C++, C# and Java source files.
* <http://jastyle.sourceforge.net>
*
* jAstyle is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* jAstyle is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with jAstyle. If not, see <http://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
package decompsource.com.github.abrarsyed.jastyle;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class ASFormatter extends ASBeautifier
{
private final static String TEMP_SUFFIX = ".tmp";
private SourceMode formatterFileType = null; // initialized with an invalid type
private List<String> headers;
private List<String> nonParenHeaders;
private List<String> preDefinitionHeaders;
private List<String> preCommandHeaders;
private List<String> operators;
private List<String> assignmentOperators;
private List<String> castOperators;
private ASSourceIterator sourceIterator;
private ASEnhancer enhancer;
private Stack<String> preBracketHeaderStack;
private Stack<Integer> bracketTypeStack;
private Stack<Integer> parenStack;
private StringBuilder readyFormattedLine;
private StringBuilder currentLine;
private StringBuilder formattedLine;
private String currentHeader;
private char currentChar;
private char previousChar;
private char previousNonWSChar;
private char previousCommandChar;
private char quoteChar;
private int charNum;
private int preprocBracketTypeStackSize;
private int tabIncrementIn;
private int spacePadNum;
private int templateDepth;
private long formattedLineCommentNum; // comment location on formattedLine
private long previousReadyFormattedLineLength;
private EnumFormatStyle formattingStyle;
private EnumBracketMode bracketFormatMode;
private int previousBracketType;
private boolean isVirgin;
private boolean shouldPadOperators;
private boolean shouldPadParensOutside;
private boolean shouldPadParensInside;
private boolean shouldUnPadParens;
private boolean shouldConvertTabs;
private boolean isInLineComment;
private boolean isInComment;
private boolean isInPreprocessor;
private boolean isInTemplate; // true both in template definitions (e.g. template<class A>) and template usage (e.g. F<int>).
private boolean doesLineStartComment;
private boolean lineEndsInCommentOnly;
private boolean lineIsLineCommentOnly;
private boolean lineIsEmpty;
private boolean isImmediatelyPostCommentOnly;
private boolean isImmediatelyPostEmptyLine;
private boolean isInQuote;
private boolean isInVerbatimQuote;
private boolean haveLineContinuationChar;
private boolean isInQuoteContinuation;
private boolean isInBlParen;
private boolean isSpecialChar;
private boolean isNonParenHeader;
private boolean foundQuestionMark;
private boolean foundPreDefinitionHeader;
private boolean foundNamespaceHeader;
private boolean foundClassHeader;
private boolean foundInterfaceHeader;
private boolean foundPreCommandHeader;
private boolean foundCastOperator;
private boolean isInLineBreak;
private boolean endOfCodeReached;
private boolean lineCommentNoIndent;
private boolean isLineReady;
private boolean isPreviousBracketBlockRelated;
private boolean isInPotentialCalculation;
private boolean isCharImmediatelyPostComment;
private boolean isPreviousCharPostComment;
private boolean isCharImmediatelyPostLineComment;
private boolean isCharImmediatelyPostOpenBlock;
private boolean isCharImmediatelyPostCloseBlock;
private boolean isCharImmediatelyPostTemplate;
private boolean isCharImmediatelyPostReturn;
private boolean isCharImmediatelyPostOperator;
private boolean shouldBreakOneLineBlocks;
private boolean shouldReparseCurrentChar;
private boolean shouldBreakOneLineStatements;
private boolean shouldBreakClosingHeaderBrackets;
private boolean shouldBreakElseIfs;
private boolean shouldDeleteEmptyLines;
private boolean needHeaderOpeningBracket;
private boolean passedSemicolon;
private boolean passedColon;
private boolean isImmediatelyPostComment;
private boolean isImmediatelyPostLineComment;
private boolean isImmediatelyPostEmptyBlock;
private boolean isImmediatelyPostPreprocessor;
private boolean isImmediatelyPostReturn;
private boolean isImmediatelyPostOperator;
private String suffix = ".orig";
private boolean shouldBreakBlocks;
private boolean shouldBreakClosingHeaderBlocks;
private boolean isPrependPostBlockEmptyLineRequested;
private boolean isAppendPostBlockEmptyLineRequested;
private boolean prependEmptyLine;
private boolean appendOpeningBracket;
private boolean foundClosingHeader;
private boolean isInHeader;
private boolean isImmediatelyPostHeader;
private boolean isInCase;
private boolean isJavaStaticConstructor;
/**
* Constructor of ASFormatter
*/
public ASFormatter()
{
sourceIterator = null;
enhancer = new ASEnhancer();
preBracketHeaderStack = null;
bracketTypeStack = null;
parenStack = null;
lineCommentNoIndent = false;
formattingStyle = EnumFormatStyle.NONE;
bracketFormatMode = EnumBracketMode.NONE;
shouldPadOperators = true;
shouldPadParensOutside = false;
shouldPadParensInside = false;
shouldUnPadParens = false;
shouldBreakOneLineBlocks = true;
shouldBreakOneLineStatements = true;
shouldConvertTabs = false;
shouldBreakBlocks = false;
shouldBreakClosingHeaderBlocks = false;
shouldBreakClosingHeaderBrackets = false;
shouldDeleteEmptyLines = false;
shouldBreakElseIfs = false;
}
/**
* build vectors for each programing language depending on the file
* extension.
*/
private void buildLanguageVectors()
{
if (getFileType() == formatterFileType)
{
return;
}
formatterFileType = getFileType();
headers = new ArrayList<String>();
nonParenHeaders = new ArrayList<String>();
assignmentOperators = new ArrayList<String>();
operators = new ArrayList<String>();
preDefinitionHeaders = new ArrayList<String>();
preCommandHeaders = new ArrayList<String>();
castOperators = new ArrayList<String>();
ASResource.buildHeaders(headers, getFileType(), false);
ASResource.buildNonParenHeaders(nonParenHeaders, getFileType(), false);
ASResource.buildPreDefinitionHeaders(preDefinitionHeaders, getFileType());
ASResource.buildPreCommandHeaders(preCommandHeaders, getFileType());
if (operators.size() == 0)
{
ASResource.buildOperators(operators);
}
if (assignmentOperators.size() == 0)
{
ASResource.buildAssignmentOperators(assignmentOperators);
}
if (castOperators.size() == 0)
{
ASResource.buildCastOperators(castOperators);
}
}
/**
* set the variables for each preefined style. this will override any
* previous settings.
*/
public void fixOptionVariableConflicts()
{
formattingStyle.apply(this);
// cannot have both bracketIndent and block Indent
// default to bracketIndent
if (isBracketIndent() && isBlockIndent())
{
setBracketIndent(false);
}
}
/**
* initialize the ASFormatter.
* You really souldn't be calling this unless you know what your doing.
* <p/>
* init() should be called every time a ASFormatter object is to start formatting a NEW source file. init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object that will be used to iterate through the source code. This object will be deleted during the ASFormatter's destruction, and thus should not be deleted elsewhere.
*
* @param iterator a pointer to the DYNAMICALLY CREATED ASSourceIterator object.
*/
@Override
public void init(ASSourceIterator iterator)
{
buildLanguageVectors();
fixOptionVariableConflicts();
super.init(iterator);
enhancer.init(getFileType(), getIndentLength(), getIndentString(), isCaseIndent(), isEmptyLineFill());
sourceIterator = iterator;
preBracketHeaderStack = new Stack<String>();
parenStack = new Stack<Integer>();
parenStack.push(0); // parenStack must contain this default entry
bracketTypeStack = new Stack<Integer>();
bracketTypeStack.push(BracketType.NULL_TYPE);
currentHeader = null;
currentLine = new StringBuilder();
readyFormattedLine = new StringBuilder();
formattedLine = new StringBuilder();
currentChar = ' ';
previousChar = ' ';
previousCommandChar = ' ';
previousNonWSChar = ' ';
quoteChar = '"';
charNum = 0;
preprocBracketTypeStackSize = 0;
spacePadNum = 0;
previousReadyFormattedLineLength = -1;
templateDepth = 0;
previousBracketType = BracketType.NULL_TYPE;
isVirgin = true;
isInLineComment = false;
isInComment = false;
isInPreprocessor = false;
doesLineStartComment = false;
lineEndsInCommentOnly = false;
lineIsLineCommentOnly = false;
lineIsEmpty = false;
isImmediatelyPostCommentOnly = false;
isImmediatelyPostEmptyLine = false;
isInQuote = false;
isInVerbatimQuote = false;
haveLineContinuationChar = false;
isInQuoteContinuation = false;
isSpecialChar = false;
isNonParenHeader = true;
foundNamespaceHeader = false;
foundClassHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundCastOperator = false;
foundQuestionMark = false;
isInLineBreak = false;
endOfCodeReached = false;
isLineReady = false;
isPreviousBracketBlockRelated = true;
isInPotentialCalculation = false;
shouldReparseCurrentChar = false;
needHeaderOpeningBracket = false;
passedSemicolon = false;
passedColon = false;
isInTemplate = false;
isInBlParen = false;
isImmediatelyPostComment = false;
isImmediatelyPostLineComment = false;
isImmediatelyPostEmptyBlock = false;
isImmediatelyPostPreprocessor = false;
isImmediatelyPostReturn = false;
isImmediatelyPostOperator = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostOperator = false;
isPrependPostBlockEmptyLineRequested = false;
isAppendPostBlockEmptyLineRequested = false;
prependEmptyLine = false;
appendOpeningBracket = false;
foundClosingHeader = false;
previousReadyFormattedLineLength = 0;
isImmediatelyPostHeader = false;
isInHeader = false;
isInCase = false;
isJavaStaticConstructor = false;
}
/**
* Open input file, format it, and close the output.
*
* @param file the file to be processed
* @return true if the file was formatted, false if it was not (no changes).
*/
public boolean formatFile(File file)
{
try
{
// Unless a specific language mode has been set, set the language mode
// according to the file's suffix.
SourceMode fileTypeOld = fileType;
if (fileType == null)
{
setSourceStyle(SourceMode.getFromFileName(file.getName()));
}
// open input file
Reader in = new BufferedReader(new FileReader(file));
// open tmp file
File tempFile = new File(file.getPath() + TEMP_SUFFIX);
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(tempFile)));
format(in, out);
out.flush();
out.close();
in.close();
// reset sourcemode back to old.
setSourceStyle(fileTypeOld);
// get the new File
File outFile = file;
if (suffix != null)
{
new File(file.getPath() + suffix);
}
// you never know.. new file might exist too.
outFile.delete();
// rename
tempFile.renameTo(outFile);
return true;
}
catch (Exception e)
{
return false;
}
}
/**
* Formats the string contents of the reader and sends it out the writer.
* Niether of the streams is flushed or closed in this method.
*
* @param in Reader, where to get the input
* @param out Writer, where the formatted output goes
*/
public void format(Reader in, Writer out) throws IOException
{
ASStreamIterator streamIterator = new ASStreamIterator(in);
this.init(streamIterator);
// get a printWriter
PrintWriter pout;
if (out instanceof PrintWriter)
{
pout = (PrintWriter) out;
}
else
{
pout = new PrintWriter(out);
}
// format the file
String temp;
while (hasMoreLines())
{
temp = nextLine().toString();
pout.println(temp);
// if (formatter.hasMoreLines())
// out.print(streamIterator.getOutputEOL());
}
}
/**
* get the next formatted line.
* You shouldn't be calling this one unless you know what your doing either
*
* @return formatted line.
*/
@Override
public StringBuilder nextLine()
{
String newHeader;
boolean isInVirginLine = isVirgin;
isCharImmediatelyPostComment = false;
isPreviousCharPostComment = false;
isCharImmediatelyPostLineComment = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
isCharImmediatelyPostTemplate = false;
while (!isLineReady)
{
if (shouldReparseCurrentChar)
{
shouldReparseCurrentChar = false;
}
else if (!getNextChar())
{
breakLine(); // readyFormattedLine is assigned here
return beautify(readyFormattedLine);
}
else
// stuff to do when reading a new character...
{
// make sure that a virgin '{' at the begining ofthe file will
// be treated as a block...
if (isInVirginLine && currentChar == '{' && lineBeginsWith('{'))
{
previousCommandChar = '{';
}
isPreviousCharPostComment = isCharImmediatelyPostComment;
isCharImmediatelyPostComment = false;
isCharImmediatelyPostTemplate = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostOperator = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
}
// if (inLineNumber >= 9)
// int x = 1;
if (isInLineComment)
{
appendCurrentChar();
// explicitely break a line when a line comment's end is found.
if (charNum + 1 == currentLine.length())
{
isInLineBreak = true;
isInLineComment = false;
isImmediatelyPostLineComment = true;
currentChar = 0; // make sure it is a neutral char.
}
continue;
}
else if (isInComment)
{
if (isSequenceReached("*/"))
{
isInComment = false;
isImmediatelyPostComment = true;
appendSequence(ASResource.AS_CLOSE_COMMENT, true);
goForward(1);
if (doesLineStartComment && ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, charNum + 1) == -1)
{
lineEndsInCommentOnly = true;
}
}
else
{
appendCurrentChar();
}
continue;
}
// not in line comment or comment
else if (isInQuote)
{
if (isSpecialChar)
{
isSpecialChar = false;
}
else if (currentChar == '\\' && !isInVerbatimQuote)
{
if (peekNextChar() == ' ') // is this '\' at end of line
{
haveLineContinuationChar = true;
}
else
{
isSpecialChar = true;
}
}
else if (isInVerbatimQuote && currentChar == '"')
{
if (peekNextChar() == '"') // check consecutive quotes
{
appendSequence("\"\"");
goForward(1);
continue;
}
else
{
isInQuote = false;
isInVerbatimQuote = false;
}
}
else if (quoteChar == currentChar)
{
isInQuote = false;
}
appendCurrentChar();
continue;
}
if (isSequenceReached("//"))
{
if (charNum + 2 < currentLine.length() && currentLine.charAt(charNum + 2) == 0xf2) // check for
//if ( currentLine.charAt(charNum + 2) == 0xf2) // check for
// windows line
// marker
{
isAppendPostBlockEmptyLineRequested = false;
}
isInLineComment = true;
// do not indent if in column 1 or 2
if (!lineCommentNoIndent)
{
if (charNum == 0)
{
lineCommentNoIndent = true;
}
else if (charNum == 1 && currentLine.charAt(0) == ' ')
{
lineCommentNoIndent = true;
}
}
// move comment if spaces were added or deleted
if (!lineCommentNoIndent && spacePadNum != 0)
{
adjustComments();
}
formattedLineCommentNum = formattedLine.length();
// appendSequence will write the previous line
appendSequence(ASResource.AS_OPEN_LINE_COMMENT);
goForward(1);
if (shouldBreakBlocks)
{
// break before the comment if a header follows the line
// comment
// for speed, do not check if previous line is empty,
// if previous line is '{', or if previous line is a line
// comment
if (lineIsLineCommentOnly && !isImmediatelyPostEmptyLine && !(previousCommandChar == '{') && !isImmediatelyPostLineComment)
{
checkForFollowingHeader(new StringBuilder(currentLine.substring(charNum - 1)));
}
}
if (previousCommandChar == '}')
{
currentHeader = null;
}
// explicitely break a line when a line comment's end is found.
if (charNum + 1 == currentLine.length())
{
isInLineBreak = true;
isInLineComment = false;
isImmediatelyPostLineComment = true;
currentChar = 0; // make sure it is a neutral char.
}
continue;
}
else if (isSequenceReached("/*"))
{
isInComment = true;
if (spacePadNum != 0)
{
adjustComments();
}
formattedLineCommentNum = formattedLine.length();
appendSequence(ASResource.AS_OPEN_COMMENT);
goForward(1);
if (shouldBreakBlocks)
{
// break before the comment if a header follows the comment
// for speed, do not check if previous line is empty,
// if previous line is '{', or if previous line is a line
// comment
if (doesLineStartComment && !isImmediatelyPostEmptyLine && !(previousCommandChar == '{') && !isImmediatelyPostLineComment)
{
checkForFollowingHeader(new StringBuilder(currentLine.substring(charNum - 1)));
}
}
if (previousCommandChar == '}')
{
currentHeader = null;
}
continue;
}
else if (currentChar == '"' || currentChar == '\'')
{
isInQuote = true;
if (isSharpStyle() && previousChar == '@')
{
isInVerbatimQuote = true;
}
quoteChar = currentChar;
appendCurrentChar();
continue;
}
// treat these preprocessor statements as a line comment
else if (currentChar == '#')
{
if (isSequenceReached("#region") || isSequenceReached("#endregion") || isSequenceReached("#error") || isSequenceReached("#warning"))
{
isInLineComment = true;
appendCurrentChar();
continue;
}
}
// handle white space - needed to simplify the rest.
if (Character.isWhitespace(currentChar) || isInPreprocessor)
{
appendCurrentChar();
continue;
}
/* not in MIDDLE of quote or comment or white-space of any type ... */
// need to reset 'previous' chars if appending a bracket
if (appendOpeningBracket)
{
previousCommandChar = previousNonWSChar = previousChar = '{';
}
// check if in preprocessor
// ** isInPreprocessor will be automatically reset at the begining
// of a new line in getnextChar()
if (currentChar == '#')
{
isInPreprocessor = true;
processPreprocessor();
// need to fall thru here to reset the variables
}
/* not in preprocessor ... */
if (isImmediatelyPostComment)
{
isImmediatelyPostComment = false;
isCharImmediatelyPostComment = true;
}
if (isImmediatelyPostLineComment)
{
isImmediatelyPostLineComment = false;
isCharImmediatelyPostLineComment = true;
}
if (isImmediatelyPostReturn)
{
isImmediatelyPostReturn = false;
isCharImmediatelyPostReturn = true;
}
if (isImmediatelyPostOperator)
{
isImmediatelyPostOperator = false;
isCharImmediatelyPostOperator = true;
}
// reset isImmediatelyPostHeader information
if (isImmediatelyPostHeader)
{
isImmediatelyPostHeader = false;
// Make sure headers are broken from their succeeding blocks
// (e.g.
// if (isFoo) DoBar();
// should become
// if (isFoo)
// DoBar;
// )
// But treat else if() as a special case which should not be
// broken!
if (shouldBreakOneLineStatements && (shouldBreakOneLineBlocks || !isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE)))
{
// if may break 'else if()'s, then simply break the line
if (shouldBreakElseIfs)
{
isInLineBreak = true;
}
}
}
if (passedSemicolon) // need to break the formattedLine
{
passedSemicolon = false;
if (parenStack.peek() == 0 && currentChar != ';') // allow ;;
{
// does a one-line statement have ending comments?
if (isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE))
{
int blockEnd = currentLine.lastIndexOf(ASResource.AS_CLOSE_BRACKET);
assert blockEnd != -1;
// move ending comments to this formattedLine
if (isBeforeLineEndComment(blockEnd))
{
int commentStart = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, blockEnd + 1);
assert commentStart != -1;
assert currentLine.indexOf("//", commentStart) == commentStart || currentLine.indexOf("/*", commentStart) == commentStart;
int commentLength = currentLine.length() - commentStart;
int tabCount = getIndentLength();
appendSpacePad();
formattedLine.append(ASUtils.repeat(tabCount, ' '));
formattedLine.append(currentLine.substring(commentStart, commentStart + commentLength));
currentLine.delete(commentStart, commentStart + commentLength);
}
}
shouldReparseCurrentChar = true;
isInLineBreak = true;
if (needHeaderOpeningBracket)
{
isCharImmediatelyPostCloseBlock = true;
needHeaderOpeningBracket = false;
}
continue;
}
}
if (passedColon)
{
passedColon = false;
if (parenStack.peek() == 0 && !isBeforeComment())
{
shouldReparseCurrentChar = true;
isInLineBreak = true;
continue;
}
}
// Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
// If so, set isInTemplate to true
if (!isInTemplate && currentChar == '<')
{
int maxTemplateDepth = 0;
templateDepth = 0;
for (int i = charNum; i < currentLine.length(); i++)
{
char currentChar = currentLine.charAt(i);
if (currentChar == '<')
{
templateDepth++;
maxTemplateDepth++;
}
else if (currentChar == '>')
{
templateDepth--;
if (templateDepth == 0)
{
// this is a template!
isInTemplate = true;
templateDepth = maxTemplateDepth;
break;
}
}
else if (currentChar == ',' // comma, e.g. A<int, char>
|| currentChar == '&' // reference, e.g. A<int&>
|| currentChar == '*' // pointer, e.g. A<int*>
|| currentChar == ':' // ::, e.g. std::String
|| currentChar == '[' // [] e.g. String[]
|| currentChar == ']') // [] e.g. String[]
{
continue;
}
else if (!isLegalNameChar(currentChar) && !Character.isWhitespace(currentChar))
{
// this is not a template -> leave...
isInTemplate = false;
break;
}
}
}
// handle parenthesies
if (currentChar == '(' || currentChar == '[' || isInTemplate && currentChar == '<')
{
parenStack.set(parenStack.size() - 1, Integer.valueOf(parenStack.peek().intValue() + 1));
if (currentChar == '[')
{
isInBlParen = true;
}
}
else if (currentChar == ')' || currentChar == ']' || isInTemplate && currentChar == '>')
{
parenStack.set(parenStack.size() - 1, Integer.valueOf(parenStack.peek().intValue() - 1));
if (isInTemplate && currentChar == '>')
{
templateDepth--;
if (templateDepth == 0)
{
isInTemplate = false;
isCharImmediatelyPostTemplate = true;
}
}
// check if this parenthesis closes a header, e.g. if (...),
// while (...)
if (isInHeader && parenStack.peek() == 0)
{
isInHeader = false;
isImmediatelyPostHeader = true;
}
if (currentChar == ']')
{
isInBlParen = false;
}
if (currentChar == ')')
{
foundCastOperator = false;
}
}
// handle brackets
if (currentChar == '{' || currentChar == '}')
{
if (currentChar == '{')
{
int newBracketType = getBracketType();
foundNamespaceHeader = false;
foundClassHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
isInPotentialCalculation = false;
isJavaStaticConstructor = false;
needHeaderOpeningBracket = false;
bracketTypeStack.add(newBracketType);
preBracketHeaderStack.add(currentHeader);
currentHeader = null;
isPreviousBracketBlockRelated = !isBracketType(newBracketType, BracketType.ARRAY_TYPE);
}
// this must be done before the bracketTypeStack is popped
int bracketType = bracketTypeStack.peek();
boolean isOpeningArrayBracket = isBracketType(bracketType, BracketType.ARRAY_TYPE) && bracketTypeStack.size() >= 2 && !isBracketType(bracketTypeStack.get(bracketTypeStack.size() - 2), BracketType.ARRAY_TYPE);
if (currentChar == '}')
{
// if a request has been made to append a post block empty
// line,
// but the block exists immediately before a closing
// bracket,
// then there is no need for the post block empty line.
//
isAppendPostBlockEmptyLineRequested = false;
if (bracketTypeStack.size() > 1)
{
previousBracketType = bracketTypeStack.peek();
bracketTypeStack.pop();
isPreviousBracketBlockRelated = !isBracketType(bracketType, BracketType.ARRAY_TYPE);
}
else
{
previousBracketType = BracketType.NULL_TYPE;
isPreviousBracketBlockRelated = false;
}
if (!preBracketHeaderStack.empty())
{
currentHeader = preBracketHeaderStack.peek();
preBracketHeaderStack.pop();
}
else
{
currentHeader = null;
}
}
// format brackets
if (isBracketType(bracketType, BracketType.ARRAY_TYPE))
{
formatArrayBrackets(bracketType, isOpeningArrayBracket);
}
else
{
formatBrackets(bracketType);
}
continue;
}
if ((previousCommandChar == '{' && isPreviousBracketBlockRelated || previousCommandChar == '}' && !isImmediatelyPostEmptyBlock && isPreviousBracketBlockRelated && !isPreviousCharPostComment // Fixes wrongly appended
// newlines after '}'
// immediately after
// comments
&& peekNextChar() != ' ' && !isBracketType(previousBracketType, BracketType.DEFINITION_TYPE) && !isBracketType(bracketTypeStack.peek(), BracketType.DEFINITION_TYPE)) && (shouldBreakOneLineBlocks || !isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE)))
{
isCharImmediatelyPostOpenBlock = previousCommandChar == '{';
isCharImmediatelyPostCloseBlock = previousCommandChar == '}';
if (isCharImmediatelyPostOpenBlock || isCharImmediatelyPostCloseBlock && shouldBreakOneLineStatements && isLegalNameChar(currentChar) && currentChar != '.' && !isCharImmediatelyPostComment)
{
previousCommandChar = ' ';
isInLineBreak = true;
}
}
// reset block handling flags
isImmediatelyPostEmptyBlock = false;
// look for headers
boolean isPotentialHeader = isCharPotentialHeader(currentLine, charNum);
if (isPotentialHeader && !isInTemplate)
{
newHeader = findHeader(headers);
if (newHeader != null)
{
char peekChar = peekNextChar(currentLine, charNum + newHeader.length() - 1);
// is not a header if part of a definition
if (peekChar == ',' || peekChar == ')')
{
newHeader = null;
}
// the following accessor definitions are NOT headers
// goto default; is NOT a header
else if ((newHeader.equals(ASResource.AS_GET) || newHeader.equals(ASResource.AS_SET) || newHeader.equals(ASResource.AS_DEFAULT)) && peekChar == ';')
{
newHeader = null;
}
}
if (newHeader != null)
{
foundClosingHeader = false;
String previousHeader;
// recognize closing headers of do..while, if..else,
// try..catch..finally
if (currentHeader != null)
{
if (newHeader.equals(ASResource.AS_ELSE) && currentHeader.equals(ASResource.AS_IF) || newHeader.equals(ASResource.AS_WHILE) && currentHeader.equals(ASResource.AS_DO) || newHeader.equals(ASResource.AS_CATCH) && currentHeader.equals(ASResource.AS_TRY) || newHeader.equals(ASResource.AS_CATCH) && currentHeader.equals(ASResource.AS_CATCH) || newHeader.equals(ASResource.AS_FINALLY) && currentHeader.equals(ASResource.AS_TRY) || newHeader.equals(ASResource.AS_FINALLY) && currentHeader.equals(ASResource.AS_CATCH) || newHeader.equals(ASResource.AS_SET) && currentHeader.equals(ASResource.AS_GET) || newHeader.equals(ASResource.AS_REMOVE) && currentHeader.equals(ASResource.AS_ADD))
{
foundClosingHeader = true;
}
}
previousHeader = currentHeader;
currentHeader = newHeader;
needHeaderOpeningBracket = true;
if (foundClosingHeader && previousNonWSChar == '}' && (shouldBreakOneLineBlocks || !isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE)))
{
if (bracketFormatMode.equals(EnumBracketMode.BREAK))
{
isInLineBreak = true;
}
else if (bracketFormatMode == EnumBracketMode.NONE)
{
if (shouldBreakClosingHeaderBrackets || isBracketIndent() || isBlockIndent())
{
isInLineBreak = true;
}
else
{
appendSpacePad();
// is closing bracket broken?
int i = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, 0);
if (i != -1 && currentLine.charAt(i) == '}')
{
isInLineBreak = false;
}
if (shouldBreakBlocks)
{
isAppendPostBlockEmptyLineRequested = false;
}
}
}
// bracketFormatMode == ATTACH, LINUX,
// STROUSTRUP
else
{
if (shouldBreakClosingHeaderBrackets || isBracketIndent() || isBlockIndent())
{
isInLineBreak = true;
}
else
{
spacePadNum = 0; // don't count as padding
int firstChar = ASUtils.findFirstNotOf(formattedLine, ASUtils.WHITE_SPACE, 0);
if (firstChar != -1) // if a blank line does not
// preceed this
{
isInLineBreak = false;
appendSpacePad();
}
if (shouldBreakBlocks)
{
isAppendPostBlockEmptyLineRequested = false;
}
}
}
}
// check if the found header is non-paren header
isNonParenHeader = nonParenHeaders.contains(newHeader);
// join 'else if' statements
if (previousHeader != null && currentHeader.equals(ASResource.AS_IF) && previousHeader.equals(ASResource.AS_ELSE) && isInLineBreak && !shouldBreakElseIfs)
{
// 'else' must be last thing on the line, but must not
// be #else
int start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0;
if (formattedLine.indexOf("else", start) != -1 && formattedLine.indexOf("#else", start) == -1)
{
appendSpacePad();
isInLineBreak = false;
}
}
appendSequence(currentHeader, true);
goForward(currentHeader.length() - 1);
// if a paren-header is found add a space after it, if
// needed
// this checks currentLine, appendSpacePad() checks
// formattedLine
// in C# 'catch' can be either a paren or non-paren header
if ((!isNonParenHeader || currentHeader.equals(ASResource.AS_CATCH) && peekNextChar() == '(') && !Character.isWhitespace(currentLine.charAt(charNum + 1)))
{
appendSpacePad();
}
// Signal that a header has been reached
// *** But treat a closing while() (as in do...while)
// as if it were NOT a header since a closing while()
// should never have a block after it!
if (!(foundClosingHeader && currentHeader.equals(ASResource.AS_WHILE)))
{
isInHeader = true;
// in C# 'catch' can be a paren or non-paren header
if (isNonParenHeader && peekNextChar() != '(')
{
isImmediatelyPostHeader = true;
isInHeader = false;
}
}
if (shouldBreakBlocks && (shouldBreakOneLineBlocks || !isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE)))
{
if (previousHeader == null && !foundClosingHeader && !isCharImmediatelyPostOpenBlock && !isImmediatelyPostCommentOnly)
{
isPrependPostBlockEmptyLineRequested = true;
}
if (currentHeader.equals(ASResource.AS_ELSE) || currentHeader.equals(ASResource.AS_CATCH) || currentHeader.equals(ASResource.AS_FINALLY) || foundClosingHeader)
{
isPrependPostBlockEmptyLineRequested = false;
}
if (shouldBreakClosingHeaderBlocks && isCharImmediatelyPostCloseBlock && !isImmediatelyPostCommentOnly && !currentHeader.equals(ASResource.AS_WHILE)) // closing
// do-while
// block
{
isPrependPostBlockEmptyLineRequested = true;
}
}
continue;
}
else if ((newHeader = findHeader(preDefinitionHeaders)) != null && parenStack.peek() == 0)
{
if (newHeader.equals(ASResource.AS_NAMESPACE))
{
foundNamespaceHeader = true;
}
if (newHeader.equals(ASResource.AS_CLASS))
{
foundClassHeader = true;
}
if (newHeader.equals(ASResource.AS_INTERFACE))
{
foundInterfaceHeader = true;
}
foundPreDefinitionHeader = true;
appendSequence(newHeader);
goForward(newHeader.length() - 1);
continue;
}
else if ((newHeader = findHeader(preCommandHeaders)) != null)
{
if (!(newHeader.equals(ASResource.AS_CONST) && previousCommandChar != ')')) // ''
// member
// functions
// is
// a
// command
// bracket
{
foundPreCommandHeader = true;
}
appendSequence(newHeader);
goForward(newHeader.length() - 1);
continue;
}
else if ((newHeader = findHeader(castOperators)) != null)
{
foundCastOperator = true;
appendSequence(newHeader);
goForward(newHeader.length() - 1);
continue;
}
} // (isPotentialHeader && !isInTemplate)
if (isInLineBreak) // OK to break line here
{
breakLine();
}
if (previousNonWSChar == '}' || currentChar == ';')
{
if (shouldBreakOneLineStatements && currentChar == ';' && (shouldBreakOneLineBlocks || !isBracketType(bracketTypeStack.peek(), BracketType.SINGLE_LINE_TYPE)))
{
passedSemicolon = true;
}
// append post block empty line for unbracketed header
if (shouldBreakBlocks && currentChar == ';' && currentHeader != null && parenStack.peek() == 0)
{
isAppendPostBlockEmptyLineRequested = true;
}
// end of block if a closing bracket was found
// or an opening bracket was not found (';' closes)
if (currentChar != ';' || needHeaderOpeningBracket && parenStack.peek() == 0)
{
currentHeader = null;
}
foundQuestionMark = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundCastOperator = false;
isInPotentialCalculation = false;
isNonInStatementArray = false;
isSharpAccessor = false;
}
if (currentChar == ':' && shouldBreakOneLineStatements)
{
if (isInCase && previousChar != ':' // not part of '::'
&& peekNextChar() != ':') // not part of '::'
{
isInCase = false;
passedColon = true;
}
else if (isCStyle() // for C/C++ only
&& !foundQuestionMark // not in a ... ? ... : ...
// sequence
&& !foundPreDefinitionHeader // not in a definition
// block (e.g. class foo
// : public bar
&& previousCommandChar != ')' // not immediately after
// closing paren of a
// method header, e.g.
// ASFormatter(...) :
// ASBeautifier(...)
&& previousChar != ':' // not part of '::'
&& peekNextChar() != ':' // not part of '::'
&& !Character.isDigit(peekNextChar())) // not a bit
// field
{
passedColon = true;
}
}
if (currentChar == '?')
{
foundQuestionMark = true;
}
if (isPotentialHeader && !isInTemplate)
{
if (findKeyword(currentLine, charNum, ASResource.AS_CASE) || findKeyword(currentLine, charNum, ASResource.AS_DEFAULT))
{
isInCase = true;
}
if (findKeyword(currentLine, charNum, ASResource.AS_NEW))
{
isInPotentialCalculation = false;
}
if (findKeyword(currentLine, charNum, ASResource.AS_RETURN))
{
isImmediatelyPostReturn = true;
}
if (findKeyword(currentLine, charNum, ASResource.AS_OPERATOR))
{
isImmediatelyPostOperator = true;
}
if (isJavaStyle() && findKeyword(currentLine, charNum, ASResource.AS_STATIC) && isNextCharOpeningBracket(charNum + 6))
{
isJavaStaticConstructor = true;
}
// append the entire name
String name = getCurrentWord(currentLine, charNum);
appendSequence(name);
goForward(name.length() - 1);
continue;
} // (isPotentialHeader && !isInTemplate)
// determine if this is a potential calculation
boolean isPotentialOperator = isCharPotentialOperator(currentChar);
newHeader = null;
if (isPotentialOperator)
{
newHeader = findOperator(operators);
if (newHeader != null)
{
// correct mistake of two >> closing a template
if (isInTemplate && (newHeader.equals(ASResource.AS_GR_GR) || newHeader.equals(ASResource.AS_GR_GR_GR)))
{
newHeader = ASResource.AS_GR;
}
if (!isInPotentialCalculation)
{
// must determine if newHeader is an assignment operator
// do NOT use findOperator!!!
if (assignmentOperators.contains(newHeader))
{
char peekedChar = peekNextChar();
isInPotentialCalculation = !(newHeader.equals(ASResource.AS_EQUAL) && peekedChar == '*') && !(newHeader.equals(ASResource.AS_EQUAL) && peekedChar == '&');
}
}
}
}
if (shouldPadOperators && newHeader != null)
{
padOperators(newHeader);
continue;
}
// pad commas and semi-colons
if (currentChar == ';' || currentChar == ',' && shouldPadOperators)
{
char nextChar = ' ';
if (charNum + 1 < currentLine.length())
{
nextChar = currentLine.charAt(charNum + 1);
}
if (!Character.isWhitespace(nextChar) && nextChar != '}' && nextChar != ')' && nextChar != ']' && nextChar != '>' && nextChar != ';' && !isBeforeComment()
/* && !(isBracketType(bracketTypeStack.peek(), ARRAY_TYPE)) */)
{
appendCurrentChar();
appendSpaceAfter();
continue;
}
}
if ((shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens) && (currentChar == '(' || currentChar == ')'))
{
padParens();
continue;
}
appendCurrentChar();
} // end of while loop * end of while loop * end of while loop * end of
// while loop
// return a beautified (i.e. correctly indented) line.
StringBuilder beautifiedLine;
int readyFormattedLineLength = readyFormattedLine.toString().trim().length();
if (prependEmptyLine // prepend a blank line before this formatted line
&& readyFormattedLineLength > 0 && previousReadyFormattedLineLength > 0)
{
isLineReady = true; // signal a waiting readyFormattedLine
beautifiedLine = beautify(new StringBuilder());
previousReadyFormattedLineLength = 0;
}
else
// format the current formatted line
{
isLineReady = false;
beautifiedLine = beautify(readyFormattedLine);
previousReadyFormattedLineLength = readyFormattedLineLength;
lineCommentNoBeautify = lineCommentNoIndent;
lineCommentNoIndent = false;
if (appendOpeningBracket) // insert bracket after this formatted
// line
{
appendOpeningBracket = false;
isLineReady = true; // signal a waiting readyFormattedLine
readyFormattedLine = new StringBuilder("{");
isPrependPostBlockEmptyLineRequested = false; // next line
// should not be
// empty
lineCommentNoIndent = lineCommentNoBeautify; // restore variable
lineCommentNoBeautify = false;
}
}
prependEmptyLine = false;
enhancer.enhance(beautifiedLine); // call the enhancer function
return beautifiedLine;
}
/**
* check if there are any indented lines ready to be read by nextLine()
*
* @return are there any indented lines ready?
*/
@Override
public boolean hasMoreLines()
{
return !endOfCodeReached;
}
/**
* comparison function for BracketType enum
*/
boolean isBracketType(final int a, final int b)
{
return (a & b) == b;
}
/**
* set the formatting style.
*
* @param style the formatting style.
*/
public void setFormattingStyle(EnumFormatStyle style)
{
formattingStyle = style;
}
/**
* set the bracket formatting mode. options:
*
* @param mode the bracket formatting mode.
*/
public void setBracketFormatMode(EnumBracketMode mode)
{
bracketFormatMode = mode;
}
/**
* set closing header bracket breaking mode options: true brackets just
* before closing headers (e.g. 'else', 'catch') will be broken, even if
* standard brackets are attached. false closing header brackets will be
* treated as standard brackets.
*
* @param state the closing header bracket breaking mode.
*/
public void setBreakClosingHeaderBracketsMode(boolean state)
{
shouldBreakClosingHeaderBrackets = state;
}
/**
* set 'else if()' breaking mode options: true 'else' headers will be broken
* from their succeeding 'if' headers. false 'else' headers will be attached
* to their succeeding 'if' headers.
*
* @param state the 'else if()' breaking mode.
*/
public void setBreakElseIfsMode(boolean state)
{
shouldBreakElseIfs = state;
}
/**
* set operator padding mode. options: true statement operators will be
* padded with spaces around them. false statement operators will not be
* padded.
*
* @param state the padding mode.
*/
public void setOperatorPaddingMode(boolean state)
{
shouldPadOperators = state;
}
/**
* set parenthesis outside padding mode. options: true statement
* parenthesiss will be padded with spaces around them. false statement
* parenthesiss will not be padded.
*
* @param state the padding mode.
*/
public void setParensOutsidePaddingMode(boolean state)
{
shouldPadParensOutside = state;
}
/**
* set parenthesis inside padding mode. options: true statement parenthesis
* will be padded with spaces around them. false statement parenthesis will
* not be padded.
*
* @param state the padding mode.
*/
public void setParensInsidePaddingMode(boolean state)
{
shouldPadParensInside = state;
}
/**
* set parenthesis unpadding mode. options: true statement parenthesis will
* be unpadded with spaces removed around them. false statement parenthesis
* will not be unpadded.
*
* @param state the padding mode.
*/
public void setParensUnPaddingMode(boolean state)
{
shouldUnPadParens = state;
}
/**
* set option to break/not break one-line blocks
*
* @param state true = break, false = don't break.
*/
public void setBreakOneLineBlocksMode(boolean state)
{
shouldBreakOneLineBlocks = state;
}
/**
* set option to break/not break lines consisting of multiple statements.
*
* @param state true = break, false = don't break.
*/
public void setSingleStatementsMode(boolean state)
{
shouldBreakOneLineStatements = state;
}
/**
* set option to convert tabs to spaces.
*
* @param state true = convert, false = don't convert.
*/
public void setTabSpaceConversionMode(boolean state)
{
shouldConvertTabs = state;
}
/**
* set option to break unrelated blocks of code with empty lines.
*
* @param state true = convert, false = don't convert.
*/
public void setBreakBlocksMode(boolean state)
{
shouldBreakBlocks = state;
}
/**
* set option to break closing header blocks of code (such as 'else',
* 'catch', ...) with empty lines.
*
* @param state true = convert, false = don't convert.
*/
public void setBreakClosingHeaderBlocksMode(boolean state)
{
shouldBreakClosingHeaderBlocks = state;
}
/**
* set option to delete empty lines.
*
* @param state true = delete, false = don't delete.
*/
public void setDeleteEmptyLinesMode(boolean state)
{
shouldDeleteEmptyLines = state;
}
/**
* jump over several characters.
*
* @param i the number of characters to jump over.
*/
private void goForward(int i)
{
while (--i >= 0)
{
getNextChar();
}
}
/**
* peek at the next unread character.
*
* @return the next unread character.
*/
private char peekNextChar()
{
char ch = ' ';
int peekNum = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, charNum + 1);
if (peekNum == -1)
{
return ch;
}
ch = currentLine.charAt(peekNum);
return ch;
}
/**
* check if current placement is before a comment or line-comment
*
* @return is before a comment or line-comment.
*/
private boolean isBeforeComment()
{
boolean foundComment = false;
int peekNum = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, charNum + 1);
if (peekNum == -1)
{
return foundComment;
}
foundComment = currentLine.indexOf("/*", peekNum) == peekNum || currentLine.indexOf("//", peekNum) == peekNum;
return foundComment;
}
/**
* check if current placement is before a comment or line-comment if a block
* comment it must be at the end of the line
*
* @return is before a comment or line-comment.
*/
private boolean isBeforeLineEndComment(int startPos)
{
boolean foundLineEndComment = false;
int peekNum = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, startPos + 1);
if (peekNum != -1)
{
if (currentLine.indexOf("//", peekNum) == peekNum)
{
foundLineEndComment = true;
}
else if (currentLine.indexOf("/*", peekNum) == peekNum)
{
// comment must be closed on this line with nothing after it
int endNum = currentLine.indexOf("*/", peekNum + 2);
if (endNum != -1)
{
if (ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, endNum + 2) == -1)
{
foundLineEndComment = true;
}
}
}
}
return foundLineEndComment;
}
/**
* get the next character, increasing the current placement in the process.
* the new character is inserted into the variable currentChar.
*
* @return whether succeded to recieve the new character.
*/
private boolean getNextChar()
{
isInLineBreak = false;
previousChar = currentChar;
if (!Character.isWhitespace(currentChar))
{
previousNonWSChar = currentChar;
if (!isInComment && !isInLineComment && !isInQuote && !isImmediatelyPostComment && !isImmediatelyPostLineComment && !isSequenceReached("/*") && !isSequenceReached("//"))
{
previousCommandChar = currentChar;
}
}
if (charNum + 1 < currentLine.length() && (!Character.isWhitespace(peekNextChar()) || isInComment || isInLineComment))
{
currentChar = currentLine.charAt(++charNum);
if (shouldConvertTabs && currentChar == '\t')
{
convertTabToSpaces();
}
return true;
}
// end of line has been reached
return getNextLine();
}
/**
* get the next line of input, increasing the current placement in the
* process. emptyLineWasDeleted=false
*
* @return whether succeded in reading the next line.
*/
private boolean getNextLine()
{
return getNextLine(false);
}
/**
* get the next line of input, increasing the current placement in the
* process.
*
* @param emptyLineWasDeleted the sequence to append.
* @return whether succeded in reading the next line.
*/
private boolean getNextLine(boolean emptyLineWasDeleted /* false */)
{
if (sourceIterator.hasMoreLines())
{
currentLine.delete(0, currentLine.length());
currentLine.append(sourceIterator.nextLine(emptyLineWasDeleted));
// reset variables for new line
spacePadNum = 0;
inLineNumber++;
isInCase = false;
isInQuoteContinuation = isInVerbatimQuote | haveLineContinuationChar;
haveLineContinuationChar = false;
isImmediatelyPostEmptyLine = lineIsEmpty;
previousChar = ' ';
if (currentLine.length() == 0)
{
currentLine.append(" "); // a null is inserted if this is not
// done
}
// unless reading in the first line of the file, break a new line.
if (!isVirgin)
{
isInLineBreak = true;
}
else
{
isVirgin = false;
}
// check if is in preprocessor before line trimming
// a blank line after a \ will remove the flag
isImmediatelyPostPreprocessor = isInPreprocessor;
if (previousNonWSChar != '\\' || ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, 0) == -1)
{
isInPreprocessor = false;
}
trimNewLine();
currentChar = currentLine.charAt(charNum);
if (shouldConvertTabs && currentChar == '\t')
{
convertTabToSpaces();
}
// check for an empty line inside a command bracket.
// if yes then read the next line (calls getNextLine recursively).
// must be after trimNewLine.
if (shouldDeleteEmptyLines && lineIsEmpty && isBracketType(bracketTypeStack.get(bracketTypeStack.size() - 1), BracketType.COMMAND_TYPE))
{
// but do NOT delete an empty line between comments if blocks
// are being broken
if (!(shouldBreakBlocks || shouldBreakClosingHeaderBlocks) || !isImmediatelyPostCommentOnly || !commentAndHeaderFollows())
{
isInPreprocessor = isImmediatelyPostPreprocessor; // restore
// isInPreprocessor
lineIsEmpty = false;
return getNextLine(true);
}
}
return true;
}
else
{
endOfCodeReached = true;
return false;
}
}
/**
* jump over the leading white space in the current line, IF the line does
* not begin a comment or is in a preprocessor definition.
*/
private void trimNewLine()
{
int len = currentLine.length();
int indent = getIndentLength();
charNum = 0;
tabIncrementIn = 0;
if (isInComment || isInPreprocessor || isInQuoteContinuation)
{
return;
}
while (Character.isWhitespace(currentLine.charAt(charNum)) && charNum + 1 < len)
{
if (currentLine.charAt(charNum) == '\t')
{
tabIncrementIn += indent - 1 - (tabIncrementIn + charNum) % indent;
}
++charNum;
}
isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly;
lineIsLineCommentOnly = false;
lineEndsInCommentOnly = false;
doesLineStartComment = false;
lineIsEmpty = false;
if (isSequenceReached("/*"))
{
charNum = 0;
tabIncrementIn = 0;
doesLineStartComment = true;
}
if (isSequenceReached("//"))
{
lineIsLineCommentOnly = true;
}
if (Character.isWhitespace(currentLine.charAt(charNum)) && !(charNum + 1 < currentLine.length()))
{
lineIsEmpty = true;
}
}
/**
* append a String sequence to the current formatted line. Unless disabled
* (via canBreakLine == false), first check if a line-break has been
* registered, and if so break the formatted line, and only then append the
* sequence into the next formatted line.
*
* @param sequence the sequence to append.
* @param canBreakLine if true, a registered line-break
*/
private void appendSequence(String sequence, boolean canBreakLine)
{
if (canBreakLine && isInLineBreak)
{
breakLine();
}
formattedLine.append(sequence);
}
/**
* append a String sequence to the current formatted line. In this case
* canBreakLine=true, first check if a line-break has been registered, and
* if so break the formatted line, and only then append the sequence into
* the next formatted line.
*
* @param sequence the sequence to append.
*/
private void appendSequence(String sequence)
{
appendSequence(sequence, true);
}
/**
* append a space to the current formattedline, UNLESS the last character is
* already a white-space character.
*/
private void appendSpacePad()
{
int len = formattedLine.length();
if (len > 0 && !Character.isWhitespace(formattedLine.charAt(len - 1)))
{
formattedLine.append(' ');
spacePadNum++;
}
}
/**
* append a space to the current formattedline, UNLESS the next character is
* already a white-space character.
*/
private void appendSpaceAfter()
{
int len = currentLine.length();
if (charNum + 1 < len && !Character.isWhitespace(currentLine.charAt(charNum + 1)))
{
formattedLine.append(' ');
spacePadNum++;
}
}
/**
* register a line break for the formatted line.
*/
private void breakLine()
{
isLineReady = true;
isInLineBreak = false;
spacePadNum = 0;
formattedLineCommentNum = -1;
// queue an empty line prepend request if one exists
prependEmptyLine = isPrependPostBlockEmptyLineRequested;
readyFormattedLine.delete(0, readyFormattedLine.length());
readyFormattedLine.append(formattedLine);
if (isAppendPostBlockEmptyLineRequested)
{
isAppendPostBlockEmptyLineRequested = false;
isPrependPostBlockEmptyLineRequested = true;
}
else
{
isPrependPostBlockEmptyLineRequested = false;
}
formattedLine.delete(0, formattedLine.length());
}
/**
* check if the currently reached open-bracket (i.e. '{') opens a: - a
* definition type block (such as a class or namespace), - a command block
* (such as a method block) - a static array this method takes for granted
* that the current character is an opening bracket.
*
* @return the type of the opened block.
*/
private int getBracketType()
{
assert currentChar == '{';
int returnVal;
if (previousNonWSChar == '=')
{
returnVal = BracketType.ARRAY_TYPE;
}
else if (foundPreDefinitionHeader)
{
returnVal = BracketType.DEFINITION_TYPE;
if (foundNamespaceHeader)
{
returnVal = returnVal | BracketType.NAMESPACE_TYPE;
}
else if (foundClassHeader)
{
returnVal = returnVal | BracketType.CLASS_TYPE;
}
else if (foundInterfaceHeader)
{
returnVal = returnVal | BracketType.INTERFACE_TYPE;
}
}
else
{
boolean isCommandType = foundPreCommandHeader || currentHeader != null && isNonParenHeader || previousCommandChar == ')' || previousCommandChar == ':' && !foundQuestionMark || previousCommandChar == ';' || (previousCommandChar == '{' || previousCommandChar == '}') && isPreviousBracketBlockRelated || isJavaStaticConstructor;
// C# methods containing 'get', 'set', 'add', and 'remove' do NOT
// end with parens
if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1))
{
isCommandType = true;
isSharpAccessor = true;
}
returnVal = isCommandType ? BracketType.COMMAND_TYPE : BracketType.ARRAY_TYPE;
}
if (isOneLineBlockReached())
{
returnVal = returnVal | BracketType.SINGLE_LINE_TYPE;
}
return returnVal;
}
/**
* check if the currently reached '*' or '&' character is a
* pointer-or-reference symbol, or another operator. this method takes for
* granted that the current character is either a '*' or '&'.
*
* @return whether current character is a reference-or-pointer
*/
private boolean isPointerOrReference()
{
assert currentChar == '*' || currentChar == '&';
boolean isPR;
isPR = !isInPotentialCalculation || isBracketType(bracketTypeStack.peek(), BracketType.DEFINITION_TYPE) || !isLegalNameChar(previousNonWSChar) && previousNonWSChar != ')' && previousNonWSChar != ']';
if (!isPR)
{
char nextChar = peekNextChar();
isPR |= !Character.isWhitespace(nextChar) && nextChar != '-' && nextChar != '(' && nextChar != '[' && !isLegalNameChar(nextChar);
}
return isPR;
}
/**
* check if the currently reached '+' or '-' character is a unary operator
* this method takes for granted that the current character is a '+' or '-'.
*
* @return whether the current '+' or '-' is a unary operator.
*/
private boolean isUnaryOperator()
{
assert currentChar == '+' || currentChar == '-';
return (isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar)) && previousCommandChar != '.' && previousCommandChar != '\"' && previousCommandChar != '\'' && previousCommandChar != ')' && previousCommandChar != ']';
}
/**
* check if the currently reached '+' or '-' character is part of an
* exponent, i.e. 0.2E-5.
* <p/>
* this method takes for granted that the current character is a '+' or '-'.
*
* @return whether the current '+' or '-' is in an exponent.
*/
private boolean isInExponent()
{
assert currentChar == '+' || currentChar == '-';
int formattedLineLength = formattedLine.length();
if (formattedLineLength >= 2)
{
char prevPrevFormattedChar = formattedLine.charAt(formattedLineLength - 2);
char prevFormattedChar = formattedLine.charAt(formattedLineLength - 1);
return (prevFormattedChar == 'e' || prevFormattedChar == 'E') && (prevPrevFormattedChar == '.' || Character.isDigit(prevPrevFormattedChar));
}
else
{
return false;
}
}
/**
* check if a one-line bracket has been reached, i.e. if the currently
* reached '{' character is closed with a complimentry '}' elsewhere on the
* current line, .
*
* @return has a one-line bracket been reached?
*/
private boolean isOneLineBlockReached()
{
boolean isInComment = false;
boolean isInQuote = false;
int bracketCount = 1;
int currentLineLength = currentLine.length();
char quoteChar = ' ';
for (int i = charNum + 1; i < currentLineLength; ++i)
{
char ch = currentLine.charAt(i);
if (isInComment)
{
if (currentLine.indexOf("*/", i) == i)
{
isInComment = false;
++i;
}
continue;
}
if (ch == '\\')
{
++i;
continue;
}
if (isInQuote)
{
if (ch == quoteChar)
{
isInQuote = false;
}
continue;
}
if (ch == '"' || ch == '\'')
{
isInQuote = true;
quoteChar = ch;
continue;
}
if (currentLine.indexOf("//", i) == i)
{
break;
}
if (currentLine.indexOf("/*", i) == i)
{
isInComment = true;
++i;
continue;
}
if (ch == '{')
{
++bracketCount;
}
else if (ch == '}')
{
--bracketCount;
}
if (bracketCount == 0)
{
return true;
}
}
return false;
}
/**
* check if a line begins with the specified character i.e. if the current
* line begins with a open bracket.
*
* @return true or false
*/
private boolean lineBeginsWith(char charToCheck)
{
boolean beginsWith = false;
int i = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, 0);
if (i != -1)
{
if (currentLine.charAt(i) == charToCheck && i == charNum)
{
beginsWith = true;
}
}
return beginsWith;
}
/**
* peek at the next word to determine if it is a C# non-paren header. will
* look ahead in the input file if necessary.
*
* @param startChar position on currentLine to start the search
* @return true if the next word is get or set.
*/
private boolean isNextWordSharpNonParenHeader(int startChar)
{
// look ahead to find the next non-comment text
StringBuilder nextText = peekNextText(new StringBuilder(currentLine.substring(startChar)));
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
{
return false;
}
if (findKeyword(nextText, 0, ASResource.AS_GET) || findKeyword(nextText, 0, ASResource.AS_SET) || findKeyword(nextText, 0, ASResource.AS_ADD) || findKeyword(nextText, 0, ASResource.AS_REMOVE))
{
return true;
}
return false;
}
/**
* peek at the next char to determine if it is an opening bracket. will look
* ahead in the input file if necessary. this determines a java static
* constructor.
*
* @param startChar position on currentLine to start the search
* @return true if the next word is an opening bracket.
*/
private boolean isNextCharOpeningBracket(int startChar)
{
StringBuilder nextText = peekNextText(new StringBuilder(currentLine.substring(startChar)));
return nextText.charAt(0) == '{';
}
/**
* get the next non-whitespace subString on following lines, bypassing all
* comments. endOnEmptyLine = false
*
* @param firstLine the first line to check
* @return the next non-whitespace subString.
*/
private StringBuilder peekNextText(StringBuilder firstLine)
{
return peekNextText(firstLine, false);
}
/**
* get the next non-whitespace subString on following lines, bypassing all
* comments.
*
* @param firstLine the first line to check
* @return the next non-whitespace subString.
*/
private StringBuilder peekNextText(StringBuilder firstLine, boolean endOnEmptyLine /* false */)
{
boolean isFirstLine = true;
boolean needReset = false;
StringBuilder nextLine = firstLine;
int firstChar = -1;
// find the first non-blank text, bypassing all comments.
boolean isInComment = false;
while (sourceIterator.hasMoreLines())
{
if (isFirstLine)
{
isFirstLine = false;
}
else
{
nextLine = sourceIterator.peekNextLine();
needReset = true;
}
firstChar = ASUtils.findFirstNotOf(nextLine, ASUtils.WHITE_SPACE, 0);
if (firstChar == -1)
{
if (endOnEmptyLine && !isInComment)
{
break;
}
else
{
continue;
}
}
if (nextLine.indexOf("/*", firstChar) == firstChar)
{
isInComment = true;
}
if (isInComment)
{
firstChar = nextLine.indexOf("*/", firstChar);
if (firstChar == -1)
{
continue;
}
firstChar += 2;
isInComment = false;
firstChar = ASUtils.findFirstNotOf(nextLine, ASUtils.WHITE_SPACE, firstChar);
if (firstChar == -1)
{
continue;
}
}
if (nextLine.indexOf("//", firstChar) == firstChar)
{
continue;
}
// found the next text
break;
}
if (needReset)
{
sourceIterator.peekReset();
}
if (firstChar == -1)
{
nextLine = new StringBuilder("");
}
else
{
nextLine = new StringBuilder(nextLine.substring(firstChar));
}
return nextLine;
}
/**
* adjust comment position because of adding or deleting spaces the spaces
* are added or deleted to formattedLine spacePadNum contains the adjustment
*/
private void adjustComments()
{
assert spacePadNum != 0;
assert currentLine.indexOf("//", charNum) == charNum || currentLine.indexOf("/*", charNum) == charNum;
// block comment must be closed on this line with nothing after it
if (currentLine.indexOf("/*", charNum) == charNum)
{
int endNum = currentLine.indexOf("*/", charNum + 2);
if (endNum == -1)
{
return;
}
if (ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, endNum + 2) != -1)
{
return;
}
}
int len = formattedLine.length();
// if spaces were removed, need to add spaces before the comment
if (spacePadNum < 0)
{
int adjust = -spacePadNum; // make the number positive
if (formattedLine.charAt(len - 1) != '\t') // don't adjust if a tab
{
formattedLine.append(ASUtils.repeat(adjust, ' '));
}
// else // comment out to avoid compiler warning
// adjust = 0;
}
// if spaces were added, need to delete spaces before the comment, if
// possible
else if (spacePadNum > 0)
{
int adjust = spacePadNum;
if (ASUtils.findLastNotOf(formattedLine, " ") < len - adjust - 1 && formattedLine.charAt(len - 1) != '\t') // don't adjust a
// tab
{
formattedLine.setLength(len - adjust);
}
}
}
/**
* append the current bracket inside the end of line comments currentChar
* contains the bracket, it will be appended to formattedLine
* formattedLineCommentNum is the comment location on formattedLine
*/
private void appendCharInsideComments()
{
if (formattedLineCommentNum == -1 // does the comment start on the
// previous line?
|| isBeforeComment()) // does a comment follow on this line?
{
appendCurrentChar(); // don't attach
return;
}
assert formattedLine.indexOf("//", (int) formattedLineCommentNum) == (int) formattedLineCommentNum || formattedLine.indexOf("/*", (int) formattedLineCommentNum) == (int) formattedLineCommentNum;
// find the previous non space char
int end = (int) formattedLineCommentNum;
int beg = ASUtils.findLastNotOf(formattedLine, ASUtils.WHITE_SPACE, end - 1);
if (beg == -1) // is the previous line comment only?
{
appendCurrentChar(); // don't attach
return;
}
beg++;
// insert the bracket
if (end - beg < 3) // is there room to insert?
{
formattedLine.insert(beg, ASUtils.repeat(3 - end + beg, ' '));
}
if (formattedLine.charAt(beg) == '\t') // don't pad with a tab
{
formattedLine.insert(beg, ' ');
}
System.err.println(currentChar + ": " + formattedLine);
formattedLine.setCharAt(beg + 1, currentChar);
}
/**
* add or remove space padding to operators currentChar contains the paren
* the operators and necessary padding will be appended to formattedLine the
* calling function should have a continue statement after calling this
* method
*
* @param *newOperator the operator to be padded
*/
private void padOperators(String newOperator)
{
assert newOperator != null;
boolean shouldPad = !newOperator.equals(ASResource.AS_COLON_COLON) && !newOperator.equals(ASResource.AS_PAREN_PAREN) && !newOperator.equals(ASResource.AS_BLPAREN_BLPAREN) && !newOperator.equals(ASResource.AS_PLUS_PLUS) && !newOperator.equals(ASResource.AS_MINUS_MINUS) && !newOperator.equals(ASResource.AS_NOT) && !newOperator.equals(ASResource.AS_BIT_NOT) && !newOperator.equals(ASResource.AS_ARROW) && !(newOperator.equals(ASResource.AS_MINUS) && isInExponent()) && !((newOperator.equals(ASResource.AS_PLUS) || newOperator.equals(ASResource.AS_MINUS)) // check for unary plus or
// minus
&& (previousNonWSChar == '(' || previousNonWSChar == '=' || previousNonWSChar == ',')) && !(newOperator.equals(ASResource.AS_PLUS) && isInExponent()) && !isCharImmediatelyPostOperator && !((newOperator.equals(ASResource.AS_MULT) || newOperator.equals(ASResource.AS_BIT_AND)) && isPointerOrReference()) && !(newOperator.equals(ASResource.AS_MULT) && (previousNonWSChar == '.' || previousNonWSChar == '>'))
// check
// for
// ->
&& !((isInTemplate || isCharImmediatelyPostTemplate) && (newOperator.equals(ASResource.AS_LS) || newOperator.equals(ASResource.AS_GR))) && !(newOperator.equals(ASResource.AS_GCC_MIN_ASSIGN) && peekNextChar(currentLine, charNum + 1) == '>') && !(newOperator.equals(ASResource.AS_GR) && previousNonWSChar == '?') && !isInCase;
// pad before operator
if (shouldPad && !isInBlParen && !(newOperator.equals(ASResource.AS_COLON) && !foundQuestionMark) && !(newOperator.equals(ASResource.AS_QUESTION) && isSharpStyle() // check for C# nullable type (e.g.
// int?)
&& currentLine.indexOf(":", charNum + 1) == -1))
{
appendSpacePad();
}
appendSequence(newOperator);
goForward(newOperator.length() - 1);
// since this block handles '()' and '[]',
// the parenStack must be updated here accordingly!
if (newOperator.equals(ASResource.AS_PAREN_PAREN) || newOperator.equals(ASResource.AS_BLPAREN_BLPAREN))
{
parenStack.set(parenStack.size() - 1, parenStack.peek() - 1);
}
currentChar = newOperator.charAt(newOperator.length() - 1);
// pad after operator
// but do not pad after a '-' that is a unary-minus.
if (shouldPad
//&& currentLine.length() < charNum + 1
&& !isInBlParen
&& !isBeforeComment()
&& !(newOperator.equals(ASResource.AS_PLUS) && isUnaryOperator())
&& !(newOperator.equals(ASResource.AS_MINUS) && isUnaryOperator())
&& !peek(1, 1).equals(";") //!(currentLine.charAt(charNum + 1) == ';')
&& !peek(1, 2).equals("::") //!(currentLine.indexOf("::", charNum + 1) == charNum + 1)
&& !(newOperator.equals(ASResource.AS_QUESTION)
&& isSharpStyle() // check for C# nullable type (e.g. int?)
&& currentLine.charAt(charNum + 1) == '['))
{
appendSpaceAfter();
}
}
private String peek(int offset, int len)
{
if (charNum + offset + len > currentLine.length())
{
return "";
}
return currentLine.substring(charNum + offset, charNum + offset + len);
}
/**
* add or remove space padding to parens currentChar contains the paren the
* parens and necessary padding will be appended to formattedLine the
* calling function should have a continue statement after calling this
* method
*/
private void padParens()
{
assert currentChar == '(' || currentChar == ')';
if (currentChar == '(')
{
int spacesOutsideToDelete = formattedLine.length() - 1;
int spacesInsideToDelete = 0;
// compute spaces outside the opening paren to delete
if (shouldUnPadParens)
{
char lastChar = ' ';
boolean prevIsParenHeader = false;
int i = ASUtils.findLastNotOf(formattedLine, ASUtils.WHITE_SPACE);
if (i != -1)
{
int end = i;
spacesOutsideToDelete -= i;
lastChar = formattedLine.charAt(i);
// was last word a paren header?
int start; // start of the previous word
for (start = i; start > -1; start--)
{
if (!isLegalNameChar(formattedLine.charAt(start)))
{
break;
}
}
start++;
// if previous word is a header, it will be a paren header
String prevWord = formattedLine.substring(start, end + 1);
String prevWordH = null;
if (prevWord.length() > 0 && isCharPotentialHeader(new StringBuilder(prevWord), 0))
{
prevWordH = findHeader(formattedLine, start, headers);
}
if (prevWordH != null)
{
prevIsParenHeader = true;
}
else if (prevWord.equals("return") // don't unpad return
// statements
|| prevWord.equals("*")) // don't unpad multiply or
// pointer
{
prevIsParenHeader = true;
}
// don't unpad variables
else if (prevWord.equals("boolean") || prevWord.equals("int") || prevWord.equals("void") || prevWord.equals("void*") || prevWord.length() >= 6 // check end of word for
// _t
&& prevWord.indexOf("_t", prevWord.length() - 2) == prevWord.length() - 2 || prevWord.equals("BOOL") || prevWord.equals("DWORD") || prevWord.equals("HWND") || prevWord.equals("INT") || prevWord.equals("LPSTR") || prevWord.equals("VOID") || prevWord.equals("LPVOID"))
{
prevIsParenHeader = true;
}
}
// do not unpad operators, but leave them if already padded
if (shouldPadParensOutside || prevIsParenHeader)
{
spacesOutsideToDelete--;
}
else if (lastChar == '|' // check for ||
|| lastChar == '&' // check for &&
|| lastChar == ','
|| (lastChar == '>' && !foundCastOperator)
|| lastChar == '<'
|| lastChar == '?'
|| lastChar == ':'
|| lastChar == ';'
|| lastChar == '='
|| lastChar == '+'
|| lastChar == '-'
|| lastChar == '*'
|| lastChar == '/'
|| lastChar == '%'
|| lastChar == '^'
)
{
spacesOutsideToDelete--;
}
if (spacesOutsideToDelete > 0)
{
formattedLine.delete(i + 1, i + 1 + spacesOutsideToDelete);
spacePadNum -= spacesOutsideToDelete;
}
}
// pad open paren outside
char peekedCharOutside = peekNextChar();
if (shouldPadParensOutside)
{
if (!(currentChar == '(' && peekedCharOutside == ')'))
{
appendSpacePad();
}
}
appendCurrentChar();
// unpad open paren inside
if (shouldUnPadParens)
{
int j = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, charNum + 1);
if (j != -1)
{
spacesInsideToDelete = j - charNum - 1;
}
if (shouldPadParensInside)
{
spacesInsideToDelete--;
}
if (spacesInsideToDelete > 0)
{
currentLine.delete(charNum + 1, charNum + 1 + spacesInsideToDelete);
spacePadNum -= spacesInsideToDelete;
}
// convert tab to space if requested
if (shouldConvertTabs && currentLine.length() > charNum && currentLine.charAt(charNum + 1) == '\t')
{
currentLine.setCharAt(charNum + 1, ' ');
}
}
// pad open paren inside
char peekedCharInside = peekNextChar();
if (shouldPadParensInside)
{
if (!(currentChar == '(' && peekedCharInside == ')'))
{
appendSpaceAfter();
}
}
}
else if (currentChar == ')' /* || currentChar == ']' */)
{
int spacesOutsideToDelete = 0;
int spacesInsideToDelete = formattedLine.length();
// unpad close paren inside
if (shouldUnPadParens)
{
int i = ASUtils.findLastNotOf(formattedLine, ASUtils.WHITE_SPACE);
if (i != -1)
{
spacesInsideToDelete = formattedLine.length() - 1 - i;
}
if (shouldPadParensInside)
{
spacesInsideToDelete--;
}
if (spacesInsideToDelete > 0)
{
formattedLine.delete(i + 1, i + 1 + spacesInsideToDelete);
spacePadNum -= spacesInsideToDelete;
}
}
// pad close paren inside
if (shouldPadParensInside)
{
if (!(previousChar == '(' && currentChar == ')'))
{
appendSpacePad();
}
}
appendCurrentChar();
// unpad close paren outside
if (shouldUnPadParens)
{
// may have end of line comments
int j = ASUtils.findFirstNotOf(currentLine, ASUtils.WHITE_SPACE, charNum + 1);
if (j != -1)
{
if (currentLine.charAt(j) == '[' || currentLine.charAt(j) == ']')
{
spacesOutsideToDelete = j - charNum - 1;
}
}
if (shouldPadParensOutside)
{
spacesOutsideToDelete--;
}
if (spacesOutsideToDelete > 0)
{
currentLine.delete(charNum + 1, charNum + 1 + spacesOutsideToDelete);
spacePadNum -= spacesOutsideToDelete;
}
}
// pad close paren outside
char peekedCharOutside = peekNextChar();
if (shouldPadParensOutside)
{
if (peekedCharOutside != ';' && peekedCharOutside != ',' && peekedCharOutside != '.' && peekedCharOutside != '-') // check
// for
// ->
{
appendSpaceAfter();
}
}
}
}
/**
* format brackets as attached or broken currentChar contains the bracket
* the brackets will be appended to the current formattedLine or a new
* formattedLine as necessary the calling function should have a continue
* statement after calling this method
*
* @param bracketType the type of bracket to be formatted.
*/
private void formatBrackets(int bracketType)
{
assert !isBracketType(bracketType, BracketType.ARRAY_TYPE);
assert currentChar == '{' || currentChar == '}';
if (currentChar == '{')
{
parenStack.add(0);
}
else if (currentChar == '}')
{
// parenStack must contain one entry
if (parenStack.size() > 1)
{
parenStack.pop();
}
}
if (currentChar == '{')
{
// break or attach the bracket
boolean breakBracket = false;
if (bracketFormatMode == EnumBracketMode.NONE)
{
if (lineBeginsWith('{')) // is opening bracket broken?
{
breakBracket = true;
}
}
else if (bracketFormatMode == EnumBracketMode.BREAK)
{
breakBracket = true;
}
else if (bracketFormatMode == EnumBracketMode.LINUX || bracketFormatMode == EnumBracketMode.STROUSTRUP)
{
// first entry in bracketTypeStack is NULL_TYPE
int bracketTypeStackEnd = bracketTypeStack.size() - 1;
// break a class if Linux
if (isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.CLASS_TYPE))
{
if (bracketFormatMode == EnumBracketMode.LINUX)
{
breakBracket = true;
}
}
// break a namespace or interface if Linux
else if (isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.NAMESPACE_TYPE) || isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.INTERFACE_TYPE))
{
if (bracketFormatMode == EnumBracketMode.LINUX)
{
breakBracket = true;
}
}
// break the first bracket if a function
else if (bracketTypeStackEnd == 1 && isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.COMMAND_TYPE))
{
breakBracket = true;
}
else if (bracketTypeStackEnd > 1)
{
// break the first bracket after a namespace if a function
if (isBracketType(bracketTypeStack.get(bracketTypeStackEnd - 1), BracketType.NAMESPACE_TYPE))
{
if (isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.COMMAND_TYPE))
{
breakBracket = true;
}
}
// if not C style then break the first bracket after a class
// if a function
else if (!isCStyle())
{
if (isBracketType(bracketTypeStack.get(bracketTypeStackEnd - 1), BracketType.CLASS_TYPE) && isBracketType(bracketTypeStack.get(bracketTypeStackEnd), BracketType.COMMAND_TYPE))
{
breakBracket = true;
}
}
}
}
if (breakBracket)
{
if (isBeforeComment() && (shouldBreakOneLineBlocks || !isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE)))
{
// if comment is at line end leave the comment on this line
if (isBeforeLineEndComment(charNum) && !lineBeginsWith('{'))
{
currentChar = ' '; // remove bracket from current line
appendOpeningBracket = true; // append bracket to
// following line
}
// else put comment after the bracket
else
{
breakLine();
}
}
else if (!isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE))
{
breakLine();
}
else if (shouldBreakOneLineBlocks && peekNextChar() != '}')
{
breakLine();
}
else if (!isInLineBreak)
{
appendSpacePad();
}
appendCurrentChar();
}
else
// attach bracket
{
// are there comments before the bracket?
if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
{
if ((shouldBreakOneLineBlocks || !isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE)) && peekNextChar() != '}' && previousCommandChar != '{' // don't attach { {
&& previousCommandChar != '}' // don't attach } {
&& previousCommandChar != ';') // don't attach ; {
{
appendCharInsideComments();
}
else
{
appendCurrentChar(); // don't attach
}
}
else if (previousCommandChar == '{' || previousCommandChar == '}' || previousCommandChar == ';') // '}' , ';' chars added
// for proper handling
// of '{' immediately
// after a '}' or ';'
{
appendCurrentChar(); // don't attach
}
else
{
// if a blank line preceeds this don't attach
int firstChar = ASUtils.findFirstNotOf(formattedLine, ASUtils.WHITE_SPACE, 0);
if (firstChar == -1)
{
appendCurrentChar(); // don't attach
}
else if ((shouldBreakOneLineBlocks || !isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE) || peekNextChar() == '}') && !(isImmediatelyPostPreprocessor && lineBeginsWith('{')))
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
}
else
{
if (!isInLineBreak)
{
appendSpacePad();
}
appendCurrentChar(); // don't attach
}
}
}
}
else if (currentChar == '}')
{
// mark state of immediately after empty block
// this state will be used for locating brackets that appear
// immedately AFTER an empty block (e.g. '{} \n}').
if (previousCommandChar == '{')
{
isImmediatelyPostEmptyBlock = true;
}
if (!(previousCommandChar == '{' && isPreviousBracketBlockRelated) // this
// '{'
// does
// not
// close
// an
// empty
// block
&& (shouldBreakOneLineBlocks || !isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE)) // astyle is allowed
// to break on line
// blocks
&& !isImmediatelyPostEmptyBlock) // this '}' does not
// immediately follow an
// empty block
{
breakLine();
appendCurrentChar();
}
else
{
if (!isCharImmediatelyPostComment
// && !bracketFormatMode == NONE
&& !isImmediatelyPostEmptyBlock)
{
isInLineBreak = false;
}
appendCurrentChar();
}
// if a declaration follows a definition, space pad
if (isLegalNameChar(peekNextChar()))
{
appendSpaceAfter();
}
if (shouldBreakBlocks && currentHeader != null && parenStack.peek() == 0)
{
isAppendPostBlockEmptyLineRequested = true;
}
}
}
/**
* format array brackets as attached or broken determine if the brackets can
* have an inStatement indent currentChar contains the bracket the brackets
* will be appended to the current formattedLine or a new formattedLine as
* necessary the calling function should have a continue statement after
* calling this method
*
* @param bracketType the type of bracket to be formatted, must be an ARRAY_TYPE.
* @param isOpeningArrayBracket indicates if this is the opening bracket for the array block.
*/
private void formatArrayBrackets(int bracketType, boolean isOpeningArrayBracket)
{
assert isBracketType(bracketType, BracketType.ARRAY_TYPE);
assert currentChar == '{' || currentChar == '}';
if (currentChar == '{')
{
// is this the first opening bracket in the array?
if (isOpeningArrayBracket)
{
if (bracketFormatMode == EnumBracketMode.ATTACH || bracketFormatMode == EnumBracketMode.LINUX || bracketFormatMode == EnumBracketMode.STROUSTRUP)
{
// don't attach to a preprocessor directive
if (isImmediatelyPostPreprocessor && lineBeginsWith('{'))
{
isInLineBreak = true;
appendCurrentChar(); // don't attach
}
// are there comments before the bracket?
else if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
{
appendCharInsideComments();
}
else
{
// if a blank line preceeds this don't attach
int firstChar = ASUtils.findFirstNotOf(formattedLine, ASUtils.WHITE_SPACE, 0);
if (firstChar == -1)
{
appendCurrentChar(); // don't attach
}
else
{
// if bracket is broken or not an assignment
if (lineBeginsWith('{') || previousNonWSChar != '=')
{
appendSpacePad();
}
appendCurrentChar(false); // OK to attach
}
}
}
else if (bracketFormatMode == EnumBracketMode.BREAK)
{
if (Character.isWhitespace(peekNextChar()))
{
breakLine();
}
else if (isBeforeComment())
{
// do not break unless comment is at line end
if (isBeforeLineEndComment(charNum))
{
currentChar = ' '; // remove bracket from current
// line
appendOpeningBracket = true; // append bracket to
// following line
}
}
if (!isInLineBreak && previousNonWSChar != '=')
{
appendSpacePad();
}
appendCurrentChar();
}
else if (bracketFormatMode == EnumBracketMode.NONE)
{
if (lineBeginsWith('{')) // is opening bracket broken?
{
appendCurrentChar(); // don't attach
}
else
{
// if bracket is broken or not an assignment
if (lineBeginsWith('{') || previousNonWSChar != '=')
{
appendSpacePad();
}
appendCurrentChar(false); // OK to attach
}
}
}
else
{
appendCurrentChar(); // not the first opening bracket - don't
}
// change
// if an opening bracket ends the line there will be no inStatement
// indent
char nextChar = peekNextChar();
if (Character.isWhitespace(nextChar) || isBeforeLineEndComment(charNum) || nextChar == '{')
{
isNonInStatementArray = true;
}
// Java "new Type [] {...}" IS an inStatement indent
if (isJavaStyle() && previousNonWSChar == ']')
{
isNonInStatementArray = false;
}
}
else if (currentChar == '}')
{
// does this close the first opening bracket in the array?
if (isOpeningArrayBracket && !isBracketType(bracketType, BracketType.SINGLE_LINE_TYPE))
{
breakLine();
appendCurrentChar();
}
else
{
appendCurrentChar();
}
// if a declaration follows an enum definition, space pad
if (isLegalNameChar(peekNextChar()))
{
appendSpaceAfter();
}
}
}
/**
* convert tabs to spaces. charNum points to the current character to
* convert to spaces. tabIncrementIn is the increment that must be added for
* tab indent characters to get the correct column for the current tab.
* replaces the tab in currentLine with the required number of spaces.
* replaces the value of currentChar.
*/
private void convertTabToSpaces()
{
assert currentLine.charAt(charNum) == '\t';
// do NOT replace if in quotes
if (isInQuote || isInQuoteContinuation)
{
return;
}
int indent = getIndentLength();
int numSpaces = indent - (tabIncrementIn + charNum) % indent;
String spaces = ASUtils.repeat(numSpaces, ' ');
currentLine.replace(charNum, charNum + numSpaces, spaces);
currentChar = currentLine.charAt(charNum);
}
/**
* check for a following header when a comment is reached. if a header
* follows, the comments are kept as part of the header block. firstLine
* must contain the start of the coment.
*/
private void checkForFollowingHeader(StringBuilder firstLine)
{
// look ahead to find the next non-comment text
StringBuilder nextText = peekNextText(firstLine, true);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
{
return;
}
String newHeader = findHeader(nextText, 0, headers);
if (newHeader == null)
{
return;
}
// may need to break if a header follows
boolean isClosingHeader = newHeader.equals(ASResource.AS_ELSE) || newHeader.equals(ASResource.AS_CATCH) || newHeader.equals(ASResource.AS_FINALLY);
// if a closing header, reset break unless break is requested
if (isClosingHeader)
{
if (!shouldBreakClosingHeaderBlocks)
{
isPrependPostBlockEmptyLineRequested = false;
}
}
// if an opening header, break before the comment
else
{
isPrependPostBlockEmptyLineRequested = true;
}
}
/**
* process preprocessor statements. charNum should be the index of the
* preprocessor directive.
* <p/>
* delete bracketTypeStack entries added by #if if a #else is found. prevents double entries in the bracketTypeStack.
*/
private void processPreprocessor()
{
assert currentLine.charAt(charNum) == '#';
int preproc = charNum + 1;
if (currentLine.indexOf("if", preproc) == preproc)
{
preprocBracketTypeStackSize = bracketTypeStack.size();
}
else if (currentLine.indexOf("else", preproc) == preproc)
{
// delete stack entries added in #if
// should be replaced by #else
int addedPreproc = bracketTypeStack.size() - preprocBracketTypeStackSize;
if (addedPreproc > 0)
{
for (int i = 0; i < addedPreproc; i++)
{
bracketTypeStack.pop();
}
}
}
}
/**
* determine if the next line starts a comment and a header follows the
* comment or comments
*
* @throws IOException
*/
private boolean commentAndHeaderFollows()
{
// is the next line a comment
StringBuilder nextLine = sourceIterator.peekNextLine();
int firstChar = ASUtils.findFirstNotOf(nextLine, ASUtils.WHITE_SPACE, 0);
if (firstChar == -1 || !(nextLine.indexOf("//", firstChar) == firstChar || nextLine.indexOf("/*", firstChar) == firstChar))
{
sourceIterator.peekReset();
return false;
}
// if next line is a comment, find the next non-comment text
// peekNextText will do the peekReset
StringBuilder nextText = peekNextText(nextLine, true);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
{
return false;
}
String newHeader = findHeader(nextText, 0, headers);
if (newHeader == null)
{
return false;
}
boolean isClosingHeader = newHeader.equals(ASResource.AS_ELSE) || newHeader.equals(ASResource.AS_CATCH) || newHeader.equals(ASResource.AS_FINALLY);
if (isClosingHeader && !shouldBreakClosingHeaderBlocks)
{
return false;
}
return true;
}
// call ASBase::findHeader for the current character
private String findHeader(List<String> headers)
{
return findHeader(currentLine, charNum, headers);
}
// call ASBase::findOperator for the current character
private String findOperator(List<String> headers)
{
return findOperator(currentLine.toString(), charNum, headers);
}
// append a character to the current formatted line.
private void appendChar(char ch, boolean canBreakLine)
{
if (canBreakLine && isInLineBreak)
{
breakLine();
}
formattedLine.append(ch);
isImmediatelyPostCommentOnly = false;
}
// append the CURRENT character (curentChar) to the current formatted line.
private void appendCurrentChar()
{
appendChar(currentChar, true);
}
// append the CURRENT character (curentChar) to the current formatted line.
private void appendCurrentChar(boolean canBreakLine)
{
appendChar(currentChar, canBreakLine);
}
// check if a specific sequence exists in the current placement of the
// current line
private boolean isSequenceReached(String sequence)
{
return currentLine.indexOf(sequence, charNum) == charNum;
}
public String gtSuffix()
{
return suffix;
}
public void setSuffix(String suff)
{
suffix = suff;
}
}