/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
public class ASBeautifier extends AbstractASBase
{
private List<String> headers;
private List<String> nonParenHeaders;
private List<String> preBlockStatements;
private List<String> assignmentOperators;
private List<String> nonAssignmentOperators;
private List<String> indentableHeaders;
private ASSourceIterator sourceIterator;
private Stack<ASBeautifier> waitingBeautifierStack;
private Stack<ASBeautifier> activeBeautifierStack;
private Stack<Integer> waitingBeautifierStackLengthStack;
private Stack<Integer> activeBeautifierStackLengthStack;
private Stack<String> headerStack;
private Stack<Stack<String>> tempStacks;
private Stack<Integer> blockParenDepthStack;
private Stack<Boolean> blockStatementStack;
private Stack<Boolean> parenStatementStack;
private Stack<Boolean> bracketBlockStateStack;
private Stack<Integer> inStatementIndentStack;
private Stack<Integer> inStatementIndentStackSizeStack;
private Stack<Integer> parenIndentStack;
private SourceMode beautifierFileType = null; // initialized with an invalid type
private String indentString;
private String currentHeader;
private String previousLastLineHeader;
private String probationHeader;
private boolean isInQuote;
private boolean isInVerbatimQuote;
private boolean haveLineContinuationChar;
private boolean isInComment;
private boolean isInCase;
private boolean isInQuestion;
private boolean isInStatement;
private boolean isInHeader;
private boolean isInTemplate;
private boolean isInDefine;
private boolean isInDefineDefinition;
private boolean classIndent;
private boolean isInClassHeader;
private boolean isInClassHeaderTab;
private boolean switchIndent;
private boolean caseIndent;
private boolean namespaceIndent;
private boolean bracketIndent;
private boolean blockIndent;
private boolean labelIndent;
private boolean preprocessorIndent;
private boolean isInConditional;
private boolean isMinimalConditinalIndentSet;
private boolean shouldForceTabIndentation;
private boolean emptyLineFill;
private boolean backslashEndsPrevLine;
private boolean blockCommentNoIndent;
private boolean blockCommentNoBeautify;
private boolean previousLineProbationTab;
private int minConditionalIndent;
private int parenDepth;
private int indentLength;
private int blockTabCount;
private int leadingWhiteSpaces;
private int maxInStatementIndent;
private int templateDepth;
private int prevFinalLineSpaceTabCount;
private int prevFinalLineTabCount;
private int defineTabCount;
private char quoteChar;
private char prevNonSpaceCh;
private char currentNonSpaceCh;
private char currentNonLegalCh;
private char prevNonLegalCh;
// variables set by ASFormatter - must be updated in activeBeautifierStack
protected int inLineNumber;
protected boolean lineCommentNoBeautify;
protected boolean isNonInStatementArray;
protected boolean isSharpAccessor;
/**
* ASBeautifier's constructor<br>
* Por default fileType = FileType.JAVA_TYPE
*/
public ASBeautifier()
{
waitingBeautifierStack = null;
activeBeautifierStack = null;
waitingBeautifierStackLengthStack = null;
activeBeautifierStackLengthStack = null;
headerStack = null;
tempStacks = null;
blockParenDepthStack = null;
blockStatementStack = null;
parenStatementStack = null;
bracketBlockStateStack = null;
inStatementIndentStack = null;
inStatementIndentStackSizeStack = null;
parenIndentStack = null;
sourceIterator = null;
isMinimalConditinalIndentSet = false;
shouldForceTabIndentation = false;
setSpaceIndentation(4);
setMaxInStatementIndentLength(40);
setClassIndent(false);
setSwitchIndent(false);
setCaseIndent(false);
setBlockIndent(false);
setBracketIndent(false);
setNamespaceIndent(false);
setLabelIndent(false);
setEmptyLineFill(false);
fileType = SourceMode.JAVA;
setSourceStyle(SourceMode.JAVA);
setPreprocessorIndent(false);
}
/**
* ASBeautifier's copy constructor must explicitly call the base class copy
* constructor
*
* @param other
*/
public ASBeautifier(final ASBeautifier other)
{
// these don't need to copy the stack
waitingBeautifierStack = null;
activeBeautifierStack = null;
waitingBeautifierStackLengthStack = null;
activeBeautifierStackLengthStack = null;
// vector '=' operator performs a DEEP copy of all elements in the
// vector
headerStack = other.headerStack;
tempStacks = new Stack<Stack<String>>();
for (final Iterator<Stack<String>> iter = other.tempStacks.iterator(); iter.hasNext(); )
{
tempStacks.push(iter.next());
}
blockParenDepthStack = other.blockParenDepthStack;
blockStatementStack = other.blockStatementStack;
parenStatementStack = other.parenStatementStack;
bracketBlockStateStack = other.bracketBlockStateStack;
inStatementIndentStack = other.inStatementIndentStack;
inStatementIndentStackSizeStack = other.inStatementIndentStackSizeStack;
parenIndentStack = other.parenIndentStack;
sourceIterator = other.sourceIterator;
// protected variables
// variables set by ASFormatter
// must also be updated in activeBeautifierStack
inLineNumber = other.inLineNumber;
lineCommentNoBeautify = other.lineCommentNoBeautify;
isNonInStatementArray = other.isNonInStatementArray;
isSharpAccessor = other.isSharpAccessor;
// private variables
indentString = other.indentString;
currentHeader = other.currentHeader;
previousLastLineHeader = other.previousLastLineHeader;
probationHeader = other.probationHeader;
isInQuote = other.isInQuote;
isInVerbatimQuote = other.isInVerbatimQuote;
haveLineContinuationChar = other.haveLineContinuationChar;
isInComment = other.isInComment;
isInCase = other.isInCase;
isInQuestion = other.isInQuestion;
isInStatement = other.isInStatement;
isInHeader = other.isInHeader;
isInTemplate = other.isInTemplate;
isInDefine = other.isInDefine;
isInDefineDefinition = other.isInDefineDefinition;
classIndent = other.classIndent;
isInClassHeader = other.isInClassHeader;
isInClassHeaderTab = other.isInClassHeaderTab;
switchIndent = other.switchIndent;
caseIndent = other.caseIndent;
namespaceIndent = other.namespaceIndent;
bracketIndent = other.bracketIndent;
blockIndent = other.blockIndent;
labelIndent = other.labelIndent;
preprocessorIndent = other.preprocessorIndent;
isInConditional = other.isInConditional;
isMinimalConditinalIndentSet = other.isMinimalConditinalIndentSet;
shouldForceTabIndentation = other.shouldForceTabIndentation;
emptyLineFill = other.emptyLineFill;
backslashEndsPrevLine = other.backslashEndsPrevLine;
blockCommentNoIndent = other.blockCommentNoIndent;
blockCommentNoBeautify = other.blockCommentNoBeautify;
previousLineProbationTab = other.previousLineProbationTab;
fileType = other.fileType;
minConditionalIndent = other.minConditionalIndent;
parenDepth = other.parenDepth;
indentLength = other.indentLength;
blockTabCount = other.blockTabCount;
leadingWhiteSpaces = other.leadingWhiteSpaces;
maxInStatementIndent = other.maxInStatementIndent;
templateDepth = other.templateDepth;
prevFinalLineSpaceTabCount = other.prevFinalLineSpaceTabCount;
prevFinalLineTabCount = other.prevFinalLineTabCount;
defineTabCount = other.defineTabCount;
quoteChar = other.quoteChar;
prevNonSpaceCh = other.prevNonSpaceCh;
currentNonSpaceCh = other.currentNonSpaceCh;
currentNonLegalCh = other.currentNonLegalCh;
prevNonLegalCh = other.prevNonLegalCh;
}
/*
* initialize the static vars
*/
private void initStatic()
{
if (fileType == beautifierFileType)
{
return;
}
beautifierFileType = fileType;
headers = new ArrayList<String>();
nonParenHeaders = new ArrayList<String>();
assignmentOperators = new ArrayList<String>();
nonAssignmentOperators = new ArrayList<String>();
preBlockStatements = new ArrayList<String>();
indentableHeaders = new ArrayList<String>();
ASResource.buildHeaders(headers, fileType, true);
ASResource.buildNonParenHeaders(nonParenHeaders, fileType, true);
ASResource.buildAssignmentOperators(assignmentOperators);
ASResource.buildNonAssignmentOperators(nonAssignmentOperators);
ASResource.buildPreBlockStatements(preBlockStatements, fileType);
ASResource.buildIndentableHeaders(indentableHeaders);
}
/**
* initialize the ASBeautifier.
* <p/>
* init() should be called every time a ABeautifier object is to start beautifying 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 ASBeautifier's destruction, and thus should not be deleted elsewhere.
*
* @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object.
*/
public void init(final ASSourceIterator iter)
{
sourceIterator = iter;
init();
}
/**
* initialize the ASBeautifier.
*/
void init()
{
initStatic();
init(getFileType());
waitingBeautifierStack = new Stack<ASBeautifier>();
activeBeautifierStack = new Stack<ASBeautifier>();
waitingBeautifierStackLengthStack = new Stack<Integer>();
activeBeautifierStackLengthStack = new Stack<Integer>();
headerStack = new Stack<String>();
tempStacks = new Stack<Stack<String>>();
tempStacks.add(new Stack<String>());
blockParenDepthStack = new Stack<Integer>();
blockStatementStack = new Stack<Boolean>();
parenStatementStack = new Stack<Boolean>();
bracketBlockStateStack = new Stack<Boolean>();
bracketBlockStateStack.push(true);
inStatementIndentStack = new Stack<Integer>();
inStatementIndentStackSizeStack = new Stack<Integer>();
inStatementIndentStackSizeStack.push(0);
parenIndentStack = new Stack<Integer>();
previousLastLineHeader = null;
currentHeader = null;
isInQuote = false;
isInVerbatimQuote = false;
haveLineContinuationChar = false;
isInComment = false;
isInStatement = false;
isInCase = false;
isInQuestion = false;
isInClassHeader = false;
isInClassHeaderTab = false;
isInHeader = false;
isInTemplate = false;
isInConditional = false;
templateDepth = 0;
parenDepth = 0;
blockTabCount = 0;
leadingWhiteSpaces = 0;
prevNonSpaceCh = '{';
currentNonSpaceCh = '{';
prevNonLegalCh = '{';
currentNonLegalCh = '{';
quoteChar = ' ';
prevFinalLineSpaceTabCount = 0;
prevFinalLineTabCount = 0;
probationHeader = null;
backslashEndsPrevLine = false;
isInDefine = false;
isInDefineDefinition = false;
defineTabCount = 0;
lineCommentNoBeautify = false;
blockCommentNoIndent = false;
blockCommentNoBeautify = false;
previousLineProbationTab = false;
isNonInStatementArray = false;
isSharpAccessor = false;
inLineNumber = 0;
}
/**
* set indentation style to C/C++.
*/
public void setSourceStyle(SourceMode mode)
{
fileType = mode;
}
/**
* indent using one tab per indentation
*
* @param length
* @param forceTabs
*/
public void setTabIndentation(final int length)
{
setTabIndentation(length, false);
}
/**
* indent using one tab per indentation
*
* @param length
* @param forceTabs
*/
public void setTabIndentation(final int length, final boolean forceTabs)
{
indentString = "\t";
indentLength = length;
shouldForceTabIndentation = forceTabs;
if (!isMinimalConditinalIndentSet)
{
minConditionalIndent = indentLength * 2;
}
}
/**
* indent using a number of spaces per indentation.
*
* @param length number of spaces per indent.
*/
public void setSpaceIndentation(final int length)
{
indentString = ASUtils.repeat(length, ' ');
indentLength = length;
if (!isMinimalConditinalIndentSet)
{
minConditionalIndent = indentLength * 2;
}
}
/**
* set the maximum indentation between two lines in a multi-line statement.
*
* @param max maximum indentation length.
*/
public void setMaxInStatementIndentLength(final int max)
{
maxInStatementIndent = max;
}
/**
* set the minimum indentation between two lines in a multi-line condition.
*
* @param min minimal indentation length.
*/
public void setMinConditionalIndentLength(final int min)
{
minConditionalIndent = min;
isMinimalConditinalIndentSet = true;
}
/**
* set the state of the bracket indentation option. If true, brackets will
* be indented one additional indent.
*
* @param state state of option.
*/
public void setBracketIndent(final boolean state)
{
bracketIndent = state;
}
/**
* set the state of the block indentation option. If true, entire blocks
* will be indented one additional indent, similar to the GNU indent style.
*
* @param state state of option.
*/
public void setBlockIndent(final boolean state)
{
blockIndent = state;
}
/**
* set the state of the class indentation option. If true, C++ class
* definitions will be indented one additional indent.
*
* @param state state of option.
*/
public void setClassIndent(final boolean state)
{
classIndent = state;
}
/**
* set the state of the switch indentation option. If true, blocks of
* 'switch' statements will be indented one additional indent.
*
* @param state state of option.
*/
public void setSwitchIndent(final boolean state)
{
switchIndent = state;
}
/**
* set the state of the case indentation option. If true, lines of 'case'
* statements will be indented one additional indent.
*
* @param state state of option.
*/
public void setCaseIndent(final boolean state)
{
caseIndent = state;
}
/**
* set the state of the namespace indentation option. If true, blocks of
* 'namespace' statements will be indented one additional indent. Otherwise,
* NO indentation will be added.
*
* @param state state of option.
*/
public void setNamespaceIndent(final boolean state)
{
namespaceIndent = state;
}
/**
* set the state of the label indentation option. If true, labels will be
* indented one indent LESS than the current indentation level. If false,
* labels will be flushed to the left with NO indent at all.
*
* @param state state of option.
*/
public void setLabelIndent(boolean state)
{
labelIndent = state;
}
/**
* set the state of the preprocessor indentation option. If true, multiline
* #define statements will be indented.
*
* @param state state of option.
*/
public void setPreprocessorIndent(boolean state)
{
preprocessorIndent = state;
}
/**
* set the state of the empty line fill option. If true, empty lines will be
* filled with the whitespace. of their previous lines. If false, these
* lines will remain empty.
*
* @param state state of option.
*/
public void setEmptyLineFill(boolean state)
{
emptyLineFill = state;
}
/**
* get the file type.
*
* @return
*/
public SourceMode getFileType()
{
return fileType;
}
/**
* get the number of spaces per indent
*
* @return value of indentLength option.
*/
public int getIndentLength()
{
return indentLength;
}
/**
* get the char used for indentation, space or tab
*
* @return the char used for indentation.
*/
public String getIndentString()
{
return indentString;
}
/**
* get the state of the block indentation option.
*
* @return state of blockIndent option.
*/
public boolean isBlockIndent()
{
return blockIndent;
}
/**
* get the state of the bracket indentation option.
*
* @return state of bracketIndent option.
*/
public boolean isBracketIndent()
{
return bracketIndent;
}
/**
* get the state of the case indentation option. If true, lines of 'case'
* statements will be indented one additional indent.
*
* @return state of caseIndent option.
*/
public boolean isCaseIndent()
{
return caseIndent;
}
/**
* get the state of the empty line fill option. If true, empty lines will be
* filled with the whitespace. of their previous lines. If false, these
* lines will remain empty.
*
* @return state of emptyLineFill option.
*/
public boolean isEmptyLineFill()
{
return emptyLineFill;
}
/**
* check if there are any indented lines ready to be read by nextLine()
*
* @return are there any indented lines ready?
*/
public boolean hasMoreLines()
{
return sourceIterator.hasMoreLines();
}
/**
* get the next indented line.
*
* @return indented line.
*/
public StringBuilder nextLine()
{
return beautify(sourceIterator.nextLine());
}
/**
* beautify a line of source code. every line of source code in a source
* code file should be sent one after the other to the beautify method.
*
* @param originalLine the original unindented line.
* @return the indented line.
*/
protected StringBuilder beautify(StringBuilder originalLine)
{
StringBuilder line = null;
boolean isInLineComment = false;
boolean lineStartsInComment = false;
boolean isInClass = false;
boolean isInSwitch = false;
boolean isInOperator = false;
boolean isSpecialChar = false;
boolean haveCaseIndent = false;
boolean closingBracketReached = false;
boolean shouldIndentBrackettedLine = true;
boolean previousLineProbation = probationHeader != null;
final boolean isInQuoteContinuation = isInVerbatimQuote | haveLineContinuationChar;
char ch = ' ';
char prevCh;
char tempCh;
int tabCount = 0;
int spaceTabCount = 0;
int lineOpeningBlocksNum = 0;
int lineClosingBlocksNum = 0;
StringBuilder outBuffer = new StringBuilder(); // the newly idented line
// is bufferd here
String lastLineHeader = "";
currentHeader = null;
lineStartsInComment = isInComment;
blockCommentNoBeautify = blockCommentNoIndent;
previousLineProbationTab = false;
haveLineContinuationChar = false;
// handle and remove white spaces around the line:
// If not in comment, first find out size of white space before line,
// so that possible comments starting in the line continue in
// relation to the preliminary white-space.
if (isInQuoteContinuation)
{
// trim a single space added by ASFormatter, otherwise leave it
// alone
if (!(originalLine.length() == 1 && originalLine.charAt(0) == ' '))
{
line = new StringBuilder(originalLine);
}
}
else if (!isInComment)
{
final int strlen = originalLine.length();
leadingWhiteSpaces = 0;
for (int j = 0; j < strlen && Character.isWhitespace(originalLine.charAt(j)); j++)
{
if (originalLine.charAt(j) == '\t')
{
leadingWhiteSpaces += indentLength;
}
else
{
leadingWhiteSpaces++;
}
}
line = new StringBuilder(originalLine.toString().trim());
}
else
{
// convert leading tabs to spaces
StringBuilder spaceTabs = new StringBuilder();
spaceTabs.append(ASUtils.repeat(indentLength, ' '));
StringBuilder newLine = new StringBuilder(originalLine.length() + 16);
newLine.append(originalLine);
int strlen = newLine.length();
for (int j = 0; j < leadingWhiteSpaces && j < strlen; j++)
{
if (newLine.charAt(j) == '\t')
{
newLine.replace(j, j + 1, spaceTabs.toString());
strlen = newLine.length();
}
}
// trim the comment leaving the new leading whitespace
int trimSize = 0;
strlen = newLine.length();
while (trimSize < strlen && trimSize < leadingWhiteSpaces && Character.isWhitespace(newLine.charAt(trimSize)))
{
trimSize++;
}
while (trimSize < strlen && Character.isWhitespace(newLine.charAt(strlen - 1)))
{
strlen--;
}
line = new StringBuilder(newLine.substring(trimSize, strlen));
int spacesToDelete;
final int trimEnd = ASUtils.findLastNotOf(line, ASUtils.WHITE_SPACE);
if (trimEnd == -1)
{
spacesToDelete = line.length();
}
else
{
spacesToDelete = line.length() - 1 - trimEnd;
}
if (spacesToDelete > 0)
{
line.delete(trimEnd + 1, trimEnd + 1 + spacesToDelete);
}
}
if (line.length() == 0)
{
if (backslashEndsPrevLine) // must continue to clear variables
{
line.append(" ");
}
else if (emptyLineFill && !isInQuoteContinuation && headerStack.size() > 0)
{
return preLineWS(prevFinalLineSpaceTabCount, prevFinalLineTabCount);
}
else
{
return line;
}
}
// handle preprocessor commands
// except C# region and endregion
if (!isInComment && (line.charAt(0) == '#' || backslashEndsPrevLine) && line.indexOf("#region") != 0 && line.indexOf("#endregion") != 0)
{
if (line.charAt(0) == '#')
{
String preproc = line.toString().trim();
// When finding a multi-lined #define statement, the original
// beautifier
// 1. sets its isInDefineDefinition flag
// 2. clones a new beautifier that will be used for the actual
// indentation
// of the #define. This clone is put into the
// activeBeautifierStack in order
// to be called for the actual indentation.
// The original beautifier will have isInDefineDefinition =
// true, isInDefine = false
// The cloned beautifier will have isInDefineDefinition = true,
// isInDefine = true
if (preprocessorIndent && preproc.indexOf("define") == 0 && line.charAt(line.length() - 1) == '\\')
{
if (!isInDefineDefinition)
{
// this is the original beautifier
isInDefineDefinition = true;
// push a new beautifier into the active stack
// this beautifier will be used for the indentation of
// this define
ASBeautifier defineBeautifier = new ASBeautifier(this);
activeBeautifierStack.add(defineBeautifier);
}
else
{
// the is the cloned beautifier that is in charge of
// indenting the #define.
isInDefine = true;
}
}
else if (preproc.startsWith("if"))
{
// push a new beautifier into the stack
waitingBeautifierStackLengthStack.add(waitingBeautifierStack.size());
activeBeautifierStackLengthStack.add(activeBeautifierStack.size());
waitingBeautifierStack.add(new ASBeautifier(this));
}
else if (preproc.indexOf("else") == 0)
{
if (waitingBeautifierStack != null && !waitingBeautifierStack.empty())
{
// MOVE current waiting beautifier to active stack.
activeBeautifierStack.add(waitingBeautifierStack.pop());
}
}
else if (preproc.indexOf("elif") == 0)
{
if (waitingBeautifierStack != null && !waitingBeautifierStack.empty())
{
// append a COPY current waiting beautifier to active
// stack, WITHOUT deleting the original.
activeBeautifierStack.add(new ASBeautifier(waitingBeautifierStack.peek()));
}
}
else if (preproc.indexOf("endif") == 0)
{
int stackLength;
if (waitingBeautifierStackLengthStack != null && !waitingBeautifierStackLengthStack.empty())
{
stackLength = waitingBeautifierStackLengthStack.pop();
while (waitingBeautifierStack.size() > stackLength)
{
waitingBeautifierStack.pop();
}
}
if (!activeBeautifierStackLengthStack.empty())
{
stackLength = activeBeautifierStackLengthStack.pop();
while (activeBeautifierStack.size() > stackLength)
{
activeBeautifierStack.pop();
}
}
}
}
// check if the last char is a backslash
if (line.length() > 0)
{
backslashEndsPrevLine = line.charAt(line.length() - 1) == '\\';
}
else
{
backslashEndsPrevLine = false;
}
// check if this line ends a multi-line #define
// if so, use the #define's cloned beautifier for the line's
// indentation
// and then remove it from the active beautifier stack and delete
// it.
if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine)
{
isInDefineDefinition = false;
ASBeautifier defineBeautifier = activeBeautifierStack.pop();
return defineBeautifier.beautify(line);
}
// unless this is a multi-line #define, return this precompiler line
// as is.
if (!isInDefine && !isInDefineDefinition)
{
return originalLine;
}
}
// if there exists any worker beautifier in the activeBeautifierStack,
// then use it instead of me to indent the current line.
// variables set by ASFormatter must be updated.
if (!isInDefine && activeBeautifierStack != null && !activeBeautifierStack.empty())
{
activeBeautifierStack.peek().inLineNumber = inLineNumber;
activeBeautifierStack.peek().lineCommentNoBeautify = lineCommentNoBeautify;
activeBeautifierStack.peek().isNonInStatementArray = isNonInStatementArray;
activeBeautifierStack.peek().isSharpAccessor = isSharpAccessor;
// must return originalLine not the trimmed line
return activeBeautifierStack.peek().beautify(originalLine);
}
// calculate preliminary indentation based on data from past lines
if (!inStatementIndentStack.empty())
{
spaceTabCount = inStatementIndentStack.peek();
}
for (int i = 0; i < headerStack.size(); i++)
{
isInClass = false;
if (blockIndent)
{
// do NOT indent opening block for these headers
if (!(headerStack.get(i).equals(ASResource.AS_NAMESPACE) || headerStack.get(i).equals(ASResource.AS_CLASS) || headerStack.get(i).equals(ASResource.AS_STRUCT) || headerStack.get(i).equals(ASResource.AS_UNION) || headerStack.get(i).equals(ASResource.AS_CONST) || headerStack.get(i).equals(ASResource.AS_INTERFACE) || headerStack.get(i).equals(ASResource.AS_THROWS) || headerStack.get(i).equals(ASResource.AS_STATIC)))
{
++tabCount;
}
}
else if (!(i > 0 && !headerStack.get(i - 1).equals(ASResource.AS_OPEN_BRACKET) && headerStack.get(i).equals(ASResource.AS_OPEN_BRACKET)))
{
++tabCount;
}
if (!isJavaStyle() && !namespaceIndent && i >= 1 && headerStack.get(i - 1).equals(ASResource.AS_NAMESPACE) && headerStack.get(i).equals(ASResource.AS_OPEN_BRACKET))
{
--tabCount;
}
if (isCStyle() && i >= 1 && headerStack.get(i - 1).equals(ASResource.AS_CLASS) && headerStack.get(i).equals(ASResource.AS_OPEN_BRACKET))
{
if (classIndent)
{
++tabCount;
}
isInClass = true;
}
// is the switchIndent option is on, indent switch statements an
// additional indent.
else if (switchIndent && i > 1 && headerStack.get(i - 1).equals(ASResource.AS_SWITCH) && headerStack.get(i).equals(ASResource.AS_OPEN_BRACKET))
{
++tabCount;
isInSwitch = true;
}
// special case for static methods that throw exceptions
if (isJavaStyle() && i > 1 && headerStack.get(i).equals(ASResource.AS_THROWS) && headerStack.get(i - 1).equals(ASResource.AS_STATIC))
{
tabCount--;
}
}
if (!lineStartsInComment && isCStyle() && isInClass && classIndent && headerStack.size() >= 2 && headerStack.get(headerStack.size() - 2).equals(ASResource.AS_CLASS) && headerStack.get(headerStack.size() - 1).equals(ASResource.AS_OPEN_BRACKET) && line.charAt(0) == '}')
{
--tabCount;
}
else if (!lineStartsInComment && isInSwitch && switchIndent && headerStack.size() >= 2 && headerStack.get(headerStack.size() - 2).equals(ASResource.AS_SWITCH) && headerStack.get(headerStack.size() - 1).equals(ASResource.AS_OPEN_BRACKET) && line.charAt(0) == '}')
{
--tabCount;
}
if (isInClassHeader)
{
isInClassHeaderTab = true;
tabCount += 2;
}
if (isInConditional)
{
--tabCount;
}
// parse characters in the current line.
for (int i = 0; i < line.length(); i++)
{
outBuffer.append(line.charAt(i));
tempCh = line.charAt(i);
prevCh = ch;
ch = tempCh;
if (Character.isWhitespace(ch))
{
continue;
}
// handle special characters (i.e. backslash+character such as \n,
// \t, ...)
if (isInQuote && !isInVerbatimQuote)
{
if (isSpecialChar)
{
isSpecialChar = false;
continue;
}
if (line.indexOf("\\\\", i) == i)
{
outBuffer.append('\\');
i++;
continue;
}
if (ch == '\\')
{
if (peekNextChar(line, i) == ' ') // is this '\'
// at end of
// line
{
haveLineContinuationChar = true;
}
else
{
isSpecialChar = true;
}
continue;
}
}
else if (isInDefine && ch == '\\')
{
continue;
}
// handle quotes (such as 'x' and "Hello Dolly")
if (!(isInComment || isInLineComment) && (ch == '"' || ch == '\''))
{
if (!isInQuote)
{
quoteChar = ch;
isInQuote = true;
if (isSharpStyle() && prevCh == '@')
{
isInVerbatimQuote = true;
}
}
else if (isInVerbatimQuote && ch == '"')
{
if (peekNextChar(line, i) == '"') // check
// consecutive
// quotes
{
outBuffer.append('"');
i++;
}
else
{
isInQuote = false;
isInVerbatimQuote = false;
}
}
else if (quoteChar == ch)
{
isInQuote = false;
isInStatement = true;
continue;
}
}
if (isInQuote)
{
continue;
}
// handle comments
if (!(isInComment || isInLineComment) && line.indexOf("//", i) == i)
{
isInLineComment = true;
outBuffer.append('/');
i++;
continue;
}
else if (!(isInComment || isInLineComment) && line.indexOf("/*", i) == i)
{
isInComment = true;
outBuffer.append('*');
i++;
int j = ASUtils.findFirstNotOf(line, ASUtils.WHITE_SPACE, 0);
if (!(line.indexOf("/*", j) == j)) // does line start with comment?
{
blockCommentNoIndent = true; // if no, cannot indent continuation lines
}
continue;
}
else if ((isInComment || isInLineComment) && line.indexOf("*/", i) == i)
{
isInComment = false;
outBuffer.append('/');
i++;
blockCommentNoIndent = false; // ok to indent next comment
continue;
}
// treat C# '#region' and '#endregion' statements as a line comment
else if (isSharpStyle() && (line.indexOf("#region", i) == i || line.indexOf("#endregion", i) == i))
{
isInLineComment = true;
continue;
}
if (isInComment || isInLineComment)
{
continue;
}
// if we have reached this far then we are NOT in a comment or
// String of special character...
if (probationHeader != null)
{
if (probationHeader.equals(ASResource.AS_STATIC) || probationHeader.equals(ASResource.AS_CONST) && ch == '{' || probationHeader.equals(ASResource.AS_SYNCHRONIZED) && ch == '(')
{
// insert the probation header as a new header
isInHeader = true;
headerStack.add(probationHeader);
// handle the specific probation header
isInConditional = probationHeader.equals(ASResource.AS_SYNCHRONIZED);
isInStatement = false;
// if the probation comes from the previous line, then
// indent by 1 tab count.
if (previousLineProbation && ch == '{' && !(blockIndent && (probationHeader.equals(ASResource.AS_CONST) || probationHeader.equals(ASResource.AS_STATIC))))
{
tabCount++;
previousLineProbationTab = true;
}
previousLineProbation = false;
}
// dismiss the probation header
probationHeader = null;
}
prevNonSpaceCh = currentNonSpaceCh;
currentNonSpaceCh = ch;
if (!isLegalNameChar(ch) && ch != ',' && ch != ';')
{
prevNonLegalCh = currentNonLegalCh;
currentNonLegalCh = ch;
}
if (isInHeader)
{
isInHeader = false;
currentHeader = headerStack.peek();
}
else
{
currentHeader = null;
}
if (isCStyle() && isInTemplate && (ch == '<' || ch == '>') && findOperator(line.toString(), i, nonAssignmentOperators) == null)
{
if (ch == '<')
{
++templateDepth;
}
else if (ch == '>')
{
if (--templateDepth <= 0)
{
if (isInTemplate)
{
ch = ';';
}
else
{
ch = '\t';
}
isInTemplate = false;
templateDepth = 0;
}
}
}
// handle parenthesies
if (ch == '(' || ch == '[' || ch == ')' || ch == ']')
{
if (ch == '(' || ch == '[')
{
isInOperator = false;
// if have a struct header, this is a declaration not a
// definition
if (ch == '(' && (isInClassHeader || isInClassHeaderTab) && headerStack.size() > 0 && headerStack.peek().equals(ASResource.AS_STRUCT))
{
headerStack.pop();
isInClassHeader = false;
// -1 for isInClassHeader, -2 for isInClassHeaderTab
if (isInClassHeaderTab)
{
tabCount -= 3;
isInClassHeaderTab = false;
}
if (tabCount < 0)
{
tabCount = 0;
}
}
if (parenDepth == 0)
{
parenStatementStack.add(isInStatement);
isInStatement = true;
}
parenDepth++;
inStatementIndentStackSizeStack.add(inStatementIndentStack.size());
if (currentHeader != null)
{
registerInStatementIndent(line.toString(), i, spaceTabCount, minConditionalIndent/*
* indentLength*
* 2
*/, true);
}
else
{
registerInStatementIndent(line.toString(), i, spaceTabCount, 0, true);
}
}
else if (ch == ')' || ch == ']')
{
parenDepth--;
if (parenDepth == 0)
{
if (!parenStatementStack.empty()) // in case of
// unmatched closing
// parens
{
isInStatement = parenStatementStack.pop();
}
ch = ' ';
isInConditional = false;
}
if (!inStatementIndentStackSizeStack.empty())
{
int previousIndentStackSize = inStatementIndentStackSizeStack.peek();
inStatementIndentStackSizeStack.pop();
while (previousIndentStackSize < inStatementIndentStack.size())
{
inStatementIndentStack.pop();
}
if (!parenIndentStack.empty())
{
int poppedIndent = parenIndentStack.pop();
if (i == 0)
{
spaceTabCount = poppedIndent;
}
}
}
}
continue;
}
if (ch == '{')
{
// first, check if '{' is a block-opener or an static-array
// opener
boolean isBlockOpener = prevNonSpaceCh == '{' && bracketBlockStateStack.peek() || prevNonSpaceCh == '}' || prevNonSpaceCh == ')' || prevNonSpaceCh == ';' || peekNextChar(line, i) == '{' || isNonInStatementArray || isSharpAccessor || isInClassHeader || isInDefine && (prevNonSpaceCh == '(' || isLegalNameChar(prevNonSpaceCh));
isInClassHeader = false;
if (!isBlockOpener && currentHeader != null)
{
for (int n = 0; n < nonParenHeaders.size(); n++)
{
if (currentHeader.equals(nonParenHeaders.get(n)))
{
isBlockOpener = true;
break;
}
}
}
// TODO: TEMPORARY??? fix to give C# }) statements a full indent
// check for anonymous method
if (isBlockOpener && isSharpStyle() && !parenIndentStack.empty())
{
isBlockOpener = false;
}
bracketBlockStateStack.push(isBlockOpener);
if (!isBlockOpener)
{
inStatementIndentStackSizeStack.push(inStatementIndentStack.size());
registerInStatementIndent(line.toString(), i, spaceTabCount, 0, true);
parenDepth++;
if (i == 0)
{
shouldIndentBrackettedLine = false;
}
continue;
}
// this bracket is a block opener...
++lineOpeningBlocksNum;
if (isInClassHeaderTab)
{
isInClassHeaderTab = false;
// decrease tab count if bracket is broken
int firstChar = ASUtils.findFirstNotOf(line, ASUtils.WHITE_SPACE, 0);
if (firstChar != -1)
{
if (line.charAt(firstChar) == '{' && firstChar == i)
{
tabCount -= 2;
}
}
}
if (bracketIndent && !namespaceIndent && headerStack.size() > 0 && headerStack.peek().equals(ASResource.AS_NAMESPACE))
{
shouldIndentBrackettedLine = false;
tabCount--;
}
// do not allow inStatementIndent - should occur for Java files
// only
if (inStatementIndentStack.size() > 0)
{
spaceTabCount = 0;
inStatementIndentStack.set(inStatementIndentStack.size() - 1, 0);
}
blockParenDepthStack.push(parenDepth);
blockStatementStack.push(isInStatement);
inStatementIndentStackSizeStack.push(inStatementIndentStack.size());
if (inStatementIndentStack.size() > 0)
{
inStatementIndentStack.set(inStatementIndentStack.size() - 1, 0);
}
blockTabCount += isInStatement ? 1 : 0;
parenDepth = 0;
isInStatement = false;
tempStacks.push(new Stack<String>());
headerStack.push(ASResource.AS_OPEN_BRACKET);
lastLineHeader = ASResource.AS_OPEN_BRACKET;
continue;
}
// check if a header has been reached
boolean isPotentialHeader = isCharPotentialHeader(line, i);
if (isPotentialHeader)
{
String newHeader = findHeader(line, i, headers);
if (newHeader != null)
{
char peekChar = peekNextChar(line, i + 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
// default(int) keyword in C# is NOT a header
else if ((newHeader.equals(ASResource.AS_GET) || newHeader.equals(ASResource.AS_SET) || newHeader.equals(ASResource.AS_DEFAULT)) && (peekChar == ';' || peekChar == '('))
{
newHeader = null;
}
}
if (newHeader != null)
{
// if we reached here, then this is a header...
boolean isIndentableHeader = true;
isInHeader = true;
Stack<String> lastTempStack;
if (tempStacks.empty())
{
lastTempStack = null;
}
else
{
lastTempStack = tempStacks.peek();
}
// if a new block is opened, push a new stack into
// tempStacks to hold the
// future list of headers in the new block.
// take care of the special case: 'else if (...)'
if (newHeader.equals(ASResource.AS_IF) && lastLineHeader.equals(ASResource.AS_ELSE))
{
headerStack.pop();
}
// take care of 'else'
else if (newHeader.equals(ASResource.AS_ELSE))
{
if (lastTempStack != null)
{
int indexOfIf = lastTempStack.indexOf(ASResource.AS_IF);
if (indexOfIf != -1)
{
// recreate the header list in headerStack up to
// the previous 'if'
// from the temporary snapshot stored in
// lastTempStack.
int restackSize = lastTempStack.size() - indexOfIf - 1;
for (int r = 0; r < restackSize; r++)
{
headerStack.add(lastTempStack.pop());
}
if (!closingBracketReached)
{
tabCount += restackSize;
}
}
/*
* If the above if is not true, i.e. no 'if' before
* the 'else', then nothing beautiful will come out
* of this... I should think about inserting an
* Exception here to notify the caller of this...
*/
}
}
// check if 'while' closes a previous 'do'
else if (newHeader.equals(ASResource.AS_WHILE))
{
if (lastTempStack != null)
{
int indexOfDo = lastTempStack.indexOf(ASResource.AS_DO);
if (indexOfDo != -1)
{
// recreate the header list in headerStack up to
// the previous 'do'
// from the temporary snapshot stored in
// lastTempStack.
int restackSize = lastTempStack.size() - indexOfDo - 1;
for (int r = 0; r < restackSize; r++)
{
headerStack.add(lastTempStack.peek());
lastTempStack.pop();
}
if (!closingBracketReached)
{
tabCount += restackSize;
}
}
}
}
// check if 'catch' closes a previous 'try' or 'catch'
else if (newHeader.equals(ASResource.AS_CATCH) || newHeader.equals(ASResource.AS_FINALLY))
{
if (lastTempStack != null)
{
int indexOfTry = lastTempStack.indexOf(ASResource.AS_TRY);
if (indexOfTry == -1)
{
indexOfTry = lastTempStack.indexOf(ASResource.AS_CATCH);
}
if (indexOfTry != -1)
{
// recreate the header list in headerStack up to
// the previous 'try'
// from the temporary snapshot stored in
// lastTempStack.
int restackSize = lastTempStack.size() - indexOfTry - 1;
for (int r = 0; r < restackSize; r++)
{
headerStack.add(lastTempStack.pop());
}
if (!closingBracketReached)
{
tabCount += restackSize;
}
}
}
}
else if (newHeader.equals(ASResource.AS_CASE))
{
isInCase = true;
if (!haveCaseIndent)
{
haveCaseIndent = true;
--tabCount;
}
}
else if (newHeader.equals(ASResource.AS_DEFAULT))
{
isInCase = true;
--tabCount;
}
else if (newHeader.equals(ASResource.AS_STATIC) || newHeader.equals(ASResource.AS_SYNCHRONIZED) || newHeader.equals(ASResource.AS_CONST) && isCStyle())
{
if (!headerStack.empty() && (headerStack.peek().equals(ASResource.AS_STATIC) || headerStack.peek().equals(ASResource.AS_SYNCHRONIZED) || headerStack.peek().equals(ASResource.AS_CONST)))
{
isIndentableHeader = false;
}
else
{
isIndentableHeader = false;
probationHeader = newHeader;
}
}
else if (newHeader.equals(ASResource.AS_CONST))
{
isIndentableHeader = false;
}
else if (newHeader.equals(ASResource.AS_TEMPLATE))
{
if (isCStyle())
{
isInTemplate = true;
}
isIndentableHeader = false;
}
if (isIndentableHeader)
{
headerStack.add(newHeader);
isInStatement = false;
if (nonParenHeaders.indexOf(newHeader) == -1)
{
isInConditional = true;
}
lastLineHeader = newHeader;
}
else
{
isInHeader = false;
}
outBuffer.append(newHeader.substring(1));
i += newHeader.length() - 1;
continue;
} // newHeader != null
} // isPotentialHeader
if (ch == '?')
{
isInQuestion = true;
}
else
// special handling of 'case' statements
if (ch == ':')
{
if (line.length() > i + 1 && line.charAt(i + 1) == ':') // look
// for
// ::
{
++i;
outBuffer.append(':');
ch = ' ';
continue;
}
else if (isInQuestion)
{
isInQuestion = false;
}
//
// else if (isCStyle() && isInClassHeader)
// {
// // found a 'class A : public B' definition
// // so do nothing special
// }
//
// else if (isCStyle() && Character.isDigit(peekNextChar(line, i)))
// {
// // found a bit field
// // so do nothing special
// }
else if (isCStyle() && isInClass && prevNonSpaceCh != ')')
{
--tabCount;
// found a 'private:' or 'public:' inside a class definition
// so do nothing special
}
// else if (isJavaStyle()
// && lastLineHeader.equals(ASResource.AS_FOR))
// {
// // found a java for-each statement
// // so do nothing special
// }
else if (isCStyle() && prevNonSpaceCh == ')' && !isInCase)
{
isInClassHeader = true;
if (i == 0)
{
tabCount += 2;
}
}
else
{
currentNonSpaceCh = ';'; // so that brackets after the ':'
// will appear as block-openers
if (isInCase)
{
isInCase = false;
ch = ';'; // from here on, treat char as ';'
}
else if (isCStyle() || isSharpStyle() && peekNextChar(line, i) == ';') // is
// in
// a
// label
// (e.g.
// 'label1:')
{
if (labelIndent)
{
--tabCount; // unindent label by one indent
}
else
{
tabCount = 0; // completely flush indent to left
}
}
}
}
if ((ch == ';' || parenDepth > 0 && ch == ',') && !inStatementIndentStackSizeStack.empty())
{
while (inStatementIndentStackSizeStack.peek() + (parenDepth > 0 ? 1 : 0) < inStatementIndentStack.size())
{
inStatementIndentStack.pop();
}
}
// handle ends of statements
if (ch == ';' && parenDepth == 0 || ch == '}'/*
* || (ch == ',' &&
* parenDepth == 0)
*/)
{
if (ch == '}')
{
// first check if this '}' closes a previous block, or a
// static array...
if (!bracketBlockStateStack.empty())
{
boolean bracketBlockState = bracketBlockStateStack.pop();
if (!bracketBlockState)
{
if (!inStatementIndentStackSizeStack.empty())
{
// this bracket is a static array
int previousIndentStackSize = inStatementIndentStackSizeStack.pop();
while (previousIndentStackSize < inStatementIndentStack.size())
{
inStatementIndentStack.pop();
}
parenDepth--;
if (i == 0)
{
shouldIndentBrackettedLine = false;
}
if (!parenIndentStack.empty())
{
int poppedIndent = parenIndentStack.pop();
if (i == 0)
{
spaceTabCount = poppedIndent;
}
}
}
continue;
}
}
// this bracket is block closer...
++lineClosingBlocksNum;
if (!inStatementIndentStackSizeStack.empty())
{
inStatementIndentStackSizeStack.pop();
}
if (!blockParenDepthStack.empty())
{
parenDepth = blockParenDepthStack.pop();
isInStatement = blockStatementStack.pop();
if (isInStatement)
{
blockTabCount--;
}
}
closingBracketReached = true;
int headerPlace = headerStack.indexOf(ASResource.AS_OPEN_BRACKET);
if (headerPlace != -1)
{
String popped = headerStack.peek();
while (!popped.equals(ASResource.AS_OPEN_BRACKET))
{
headerStack.pop();
popped = headerStack.peek();
}
headerStack.pop();
// do not indent namespace bracket unless namespaces are
// indented
if (!namespaceIndent && headerStack.size() > 0 && headerStack.peek().equals(ASResource.AS_NAMESPACE))
{
shouldIndentBrackettedLine = false;
}
if (!tempStacks.empty())
{
tempStacks.pop();
}
}
ch = ' '; // needed due to cases such as '}else{', so that
// headers ('else' tn tih case) will be
// identified...
}
/*
* Create a temporary snapshot of the current block's
* header-list in the uppermost inner stack in tempStacks, and
* clear the headerStack up to the begining of the block. Thus,
* the next future statement will think it comes one indent past
* the block's '{' unless it specifically checks for a
* companion-header (such as a previous 'if' for an 'else'
* header) within the tempStacks, and recreates the temporary
* snapshot by manipulating the tempStacks.
*/
if (!tempStacks.peek().empty())
{
while (!tempStacks.peek().empty())
{
tempStacks.peek().pop();
}
}
while (!headerStack.empty() && !headerStack.peek().equals(ASResource.AS_OPEN_BRACKET))
{
tempStacks.peek().add(headerStack.pop());
}
if (parenDepth == 0 && ch == ';')
{
isInStatement = false;
}
previousLastLineHeader = null;
isInClassHeader = false;
isInQuestion = false;
continue;
}
if (isPotentialHeader)
{
// check for preBlockStatements in C/C++ ONLY if not within
// parenthesies
// (otherwise 'struct XXX' statements would be wrongly
// interpreted...)
if (!isInTemplate && !(isCStyle() && parenDepth > 0))
{
String newHeader = findHeader(line, i, preBlockStatements);
if (newHeader != null)
{
isInClassHeader = true;
if (!isSharpStyle())
{
headerStack.add(newHeader);
}
// do not need 'where' in the headerStack
// do not need second 'class' statement in a row
else if (!(newHeader.equals(ASResource.AS_WHERE) || newHeader.equals(ASResource.AS_CLASS) && headerStack.size() > 0 && headerStack.peek().equals(ASResource.AS_CLASS)))
{
headerStack.add(newHeader);
}
outBuffer.append(newHeader.substring(1));
i += newHeader.length() - 1;
continue;
}
}
String foundIndentableHeader = findHeader(line, i, indentableHeaders);
if (foundIndentableHeader != null)
{
// must bypass the header before registering the in
// statement
outBuffer.append(foundIndentableHeader.substring(1));
i += foundIndentableHeader.length() - 1;
if (!isInOperator && !isInTemplate && !isNonInStatementArray)
{
registerInStatementIndent(line.toString(), i, spaceTabCount, 0, false);
isInStatement = true;
}
continue;
}
if (isCStyle() && findKeyword(line, i, ASResource.AS_OPERATOR))
{
isInOperator = true;
}
// "new" operator is a pointer, not a calculation
if (findKeyword(line, i, ASResource.AS_NEW))
{
if (prevNonSpaceCh == '=' && isInStatement && !inStatementIndentStack.empty())
{
inStatementIndentStack.set(inStatementIndentStack.size() - 1, 0);
}
}
// append the entire name for all others
String name = getCurrentWord(line, i);
outBuffer.append(name.substring(1));
i += name.length() - 1;
continue;
}
// Handle operators
boolean isPotentialOperator = isCharPotentialOperator(ch);
if (isPotentialOperator)
{
// Check if an operator has been reached.
String foundAssignmentOp = findOperator(line.toString(), i, assignmentOperators);
String foundNonAssignmentOp = findOperator(line.toString(), i, nonAssignmentOperators);
// Since findHeader's boundry checking was not used above, it is
// possible
// that both an assignment op and a non-assignment op where
// found,
// e.g. '>>' and '>>='. If this is the case, treat the LONGER
// one as the
// found operator.
if (foundAssignmentOp != null && foundNonAssignmentOp != null)
{
if (foundAssignmentOp.length() < foundNonAssignmentOp.length())
{
foundAssignmentOp = null;
}
else
{
foundNonAssignmentOp = null;
}
}
if (foundNonAssignmentOp != null)
{
if (foundNonAssignmentOp.length() > 1)
{
outBuffer.append(foundNonAssignmentOp.substring(1));
i += foundNonAssignmentOp.length() - 1;
}
}
else if (foundAssignmentOp != null)
{
if (foundAssignmentOp.length() > 1)
{
outBuffer.append(foundAssignmentOp.substring(1));
i += foundAssignmentOp.length() - 1;
}
if (!isInOperator && !isInTemplate && !isNonInStatementArray)
{
registerInStatementIndent(line.toString(), i, spaceTabCount, 0, false);
isInStatement = true;
}
}
}
} // end of for loop * end of for loop * end of for loop * end of for
// loop
// handle special cases of unindentation:
/*
* if '{' doesn't follow an immediately previous '{' in the headerStack
* (but rather another header such as "for" or "if", then unindent it by
* one indentation relative to its block.
*/
if (!lineStartsInComment && !blockIndent && outBuffer.length() > 0 && outBuffer.charAt(0) == '{' && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum) && !(headerStack.size() > 1 && headerStack.get(headerStack.size() - 2).equals(ASResource.AS_OPEN_BRACKET)) && shouldIndentBrackettedLine)
{
--tabCount;
}
else if (!lineStartsInComment && outBuffer.length() > 0 && outBuffer.charAt(0) == '}' && shouldIndentBrackettedLine)
{
--tabCount;
}
// correctly indent one-line-blocks...
else if (!lineStartsInComment && outBuffer.length() > 0 && lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum && previousLineProbationTab)
{
--tabCount; // lineOpeningBlocksNum - (blockIndent ? 1 : 0);
}
// correctly indent class continuation lines...
else if (!lineStartsInComment && isInClassHeaderTab && !blockIndent && outBuffer.length() > 0 && lineOpeningBlocksNum == 0 && lineOpeningBlocksNum == lineClosingBlocksNum && headerStack.size() > 0 && headerStack.peek().equals(ASResource.AS_CLASS))
{
--tabCount;
}
if (tabCount < 0)
{
tabCount = 0;
}
// take care of extra bracket indentatation option...
if (!lineStartsInComment && bracketIndent && shouldIndentBrackettedLine && outBuffer.length() > 0 && (outBuffer.charAt(0) == '{' || outBuffer.charAt(0) == '}'))
{
tabCount++;
}
if (isInDefine)
{
if (outBuffer.charAt(0) == '#')
{
String preproc = outBuffer.toString().trim();
if (preproc.startsWith("define"))
{
if (!inStatementIndentStack.empty() && inStatementIndentStack.peek() > 0)
{
defineTabCount = tabCount;
}
else
{
defineTabCount = tabCount - 1;
tabCount--;
}
}
}
tabCount -= defineTabCount;
}
if (tabCount < 0)
{
tabCount = 0;
}
if (lineCommentNoBeautify || blockCommentNoBeautify || isInQuoteContinuation)
{
tabCount = spaceTabCount = 0;
}
// finally, insert indentations into begining of line
prevFinalLineSpaceTabCount = spaceTabCount;
prevFinalLineTabCount = tabCount;
if (shouldForceTabIndentation)
{
tabCount += spaceTabCount / indentLength;
spaceTabCount = spaceTabCount % indentLength;
}
outBuffer = new StringBuilder(preLineWS(spaceTabCount, tabCount).append(outBuffer));
if (lastLineHeader != null)
{
previousLastLineHeader = lastLineHeader;
}
return outBuffer;
}
private StringBuilder preLineWS(final int spaceTabCount, int tabCount)
{
int stc = spaceTabCount;
StringBuilder ws = new StringBuilder();
for (int i = 0; i < tabCount; i++)
{
ws.append(indentString);
}
while (stc-- > 0)
{
ws.append(" ");
}
return ws;
}
/**
* register an in-statement indent.
*
* @param line
* @param i
* @param spaceTabCount
* @param minIndent
* @param updateParenStack
*/
private void registerInStatementIndent(String line, int i, int spaceTabCount, int minIndent, boolean updateParenStack)
{
int inStatementIndent;
int remainingCharNum = line.length() - i;
int nextNonWSChar = getNextProgramCharDistance(line, i);
// if indent is around the last char in the line, indent instead 2
// spaces from the previous indent
if (nextNonWSChar == remainingCharNum)
{
int previousIndent = spaceTabCount;
if (!inStatementIndentStack.empty())
{
previousIndent = inStatementIndentStack.peek();
}
inStatementIndentStack.add(/* 2 */indentLength + previousIndent);
if (updateParenStack)
{
parenIndentStack.add(previousIndent);
}
return;
}
if (updateParenStack)
{
parenIndentStack.add(i + spaceTabCount);
}
inStatementIndent = i + nextNonWSChar + spaceTabCount;
if (i + nextNonWSChar < minIndent)
{
inStatementIndent = minIndent + spaceTabCount;
}
if (i + nextNonWSChar > maxInStatementIndent)
{
inStatementIndent = indentLength * 2 + spaceTabCount;
}
if (!inStatementIndentStack.empty() && inStatementIndent < inStatementIndentStack.peek())
{
inStatementIndent = inStatementIndentStack.peek();
}
if (isNonInStatementArray)
{
inStatementIndent = 0;
}
inStatementIndentStack.add(inStatementIndent);
}
/**
* get distance to the next non-white space, non-comment character in the
* line. if no such character exists, return the length remaining to the end
* of the line.
*
* @param line
* @param i
* @return
*/
private int getNextProgramCharDistance(String line, int i)
{
boolean inComment = false;
int remainingCharNum = line.length() - i;
int charDistance;
char ch;
for (charDistance = 1; charDistance < remainingCharNum; charDistance++)
{
ch = line.charAt(i + charDistance);
if (inComment)
{
if (line.indexOf("*/", i + charDistance) == i + charDistance)
{
charDistance++;
inComment = false;
}
}
else if (Character.isWhitespace(ch))
{
continue;
}
else if (ch == '/')
{
if (line.indexOf("//", i + charDistance) == i + charDistance)
{
return remainingCharNum;
}
else if (line.indexOf("/*", i + charDistance) == i + charDistance)
{
charDistance++;
inComment = true;
}
}
else
{
return charDistance;
}
}
return charDistance;
}
// check if a specific line position contains a header.
protected String findHeader(StringBuilder line, int i, List<String> possibleHeaders)
{
assert isCharPotentialHeader(line, i) : line + " is not a potential header";
// check the word
int maxHeaders = possibleHeaders.size();
for (int p = 0; p < maxHeaders; p++)
{
String header = possibleHeaders.get(p);
int end = i + header.length();
if (end > line.length())
{
continue;
}
int result = line.substring(i, end).compareTo(header);
if (result > 0)
{
continue;
}
if (result < 0)
{
break;
}
// check that this is not part of a longer word
int wordEnd = i + header.length();
if (wordEnd == line.length())
{
return header;
}
if (isLegalNameChar(line.charAt(wordEnd)))
{
continue;
}
// is not a header if part of a definition
char peekChar = peekNextChar(line, wordEnd - 1);
if (peekChar == ',' || peekChar == ')')
{
break;
}
return header;
}
return null;
}
// check if a specific line position contains an operator.
protected String findOperator(String line, int i, List<String> possibleOperators)
{
assert isCharPotentialOperator(line.charAt(i)) : line.charAt(i) + " is not a potential header";
// find the operator in the vector
// the vector contains the LONGEST operators first
// must loop thru the entire vector
int maxOperators = possibleOperators.size();
for (int p = 0; p < maxOperators; p++)
{
if (line.indexOf(possibleOperators.get(p), i) == i)
{
return possibleOperators.get(p);
}
}
return null;
}
/**
* peek at the next unread character.
*
* @param line the line to check.
* @param i the current char position on the line.
* @return the next unread character.
*/
@Override
public char peekNextChar(StringBuilder line, int i)
{
char ch = ' ';
int peekNum = ASUtils.findFirstNotOf(line, ASUtils.WHITE_SPACE, i + 1);
if (peekNum == -1)
{
return ch;
}
ch = line.charAt(peekNum);
return ch;
}
}