/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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.Stack; public class ASEnhancer extends AbstractASBase { // options from command line or options file int indentLength; boolean useTabs; boolean caseIndent; boolean emptyLineFill; // parsing variables int lineNumber; boolean isInQuote; boolean isInComment; char quoteChar; // unindent variables int bracketCount; int switchDepth; boolean lookingForCaseBracket; boolean unindentNextLine; SwitchVariables sw; Stack<SwitchVariables> swVector; // stack vector of switch variables // event table variables boolean nextLineIsEventTable; // begin event table is reached boolean isInEventTable; // need to indent an event table // stringstream for trace // ---------------------------- functions for ASEnhancer Class // ------------------------------------- /** * ASEnhancer constructor */ public ASEnhancer() { } /** * initialize the ASEnhancer. * <p/> * init() is called each time an ASFormatter object is initialized. */ void init(SourceMode fileType, int _indentLength, String _indentString, boolean _caseIndent, boolean _emptyLineFill) { // formatting variables from ASFormatter and ASBeautifier init(fileType); indentLength = _indentLength; useTabs = _indentString.charAt(0) == '\t'; caseIndent = _caseIndent; emptyLineFill = _emptyLineFill; // unindent variables lineNumber = 0; bracketCount = 0; isInComment = false; isInQuote = false; switchDepth = 0; lookingForCaseBracket = false; unindentNextLine = false; // switch struct and vector sw = new SwitchVariables(0, 0, false); swVector = new Stack<SwitchVariables>(); nextLineIsEventTable = false; isInEventTable = false; } /** * additional formatting for line of source code. every line of source code * in a source code file should be sent one after the other to this * function. indents event tables unindents the case blocks * * @param line the original formatted line will be updated if necessary. */ public void enhance(StringBuilder line) { boolean isSpecialChar = false; int lineLength = line.length(); lineNumber++; // check for beginning of event table if (nextLineIsEventTable) { isInEventTable = true; nextLineIsEventTable = false; } if (lineLength == 0 && !isInEventTable && !emptyLineFill) { return; } // test for unindent on attached brackets if (unindentNextLine) { sw.setUnindentDepth(sw.getUnindentDepth() + 1); sw.setUnindentCase(true); unindentNextLine = false; } // parse characters in the current line. for (int i = 0; i < lineLength; i++) { char ch = line.charAt(i); // bypass whitespace if (Character.isWhitespace(ch)) { continue; } // handle special characters (i.e. backslash+character such as \n, // \t, ...) if (isSpecialChar) { isSpecialChar = false; continue; } if (!isInComment && line.indexOf("\\\\", i) == i) { i++; continue; } if (!isInComment && ch == '\\') { isSpecialChar = true; continue; } // handle quotes (such as 'x' and "Hello Dolly") if (!isInComment && (ch == '"' || ch == '\'')) { if (!isInQuote) { quoteChar = ch; isInQuote = true; } else if (quoteChar == ch) { isInQuote = false; continue; } } if (isInQuote) { continue; } // handle comments if (!isInComment && line.indexOf("//", i) == i) { // check for windows line markers if (i + 2 < line.length() && line.charAt(i + 2) > 0xf0) { lineNumber--; } break; // finished with the line } else if (!isInComment && line.indexOf("/*", i) == i) { isInComment = true; i++; continue; } else if (isInComment && line.indexOf("*/", i) == i) { isInComment = false; i++; continue; } if (isInComment) { continue; } // if we have reached this far then we are NOT in a comment or // String of special characters if (line.charAt(i) == '{') { bracketCount++; } if (line.charAt(i) == '}') { bracketCount--; } boolean isPotentialKeyword = isCharPotentialHeader(line, i); // ---------------- process event tables // -------------------------------------- // check for event table begin if (isPotentialKeyword && !isJavaStyle()) { if (findKeyword(line, i, "BEGIN_EVENT_TABLE") || findKeyword(line, i, "BEGIN_MESSAGE_MAP")) { nextLineIsEventTable = true; } // check for event table end if (findKeyword(line, i, "END_EVENT_TABLE") || findKeyword(line, i, "END_MESSAGE_MAP")) { isInEventTable = false; } } // ---------------- process switch statements // --------------------------------- if (isPotentialKeyword && findKeyword(line, i, "switch")) { switchDepth++; swVector.push(sw); // save current variables sw.setSwitchBracketCount(0); sw.setUnindentCase(false); // don't clear case until end of // switch i += 5; // bypass switch statement continue; } // just want switch statements from this point if (caseIndent || switchDepth == 0) // from here just want switch // statements { continue; } if (line.charAt(i) == '{') { sw.setSwitchBracketCount(sw.getSwitchBracketCount() + 1); if (lookingForCaseBracket) // if 1st after case statement { sw.setUnindentCase(true); // unindenting this case sw.setUnindentDepth(sw.getUnindentDepth() + 1); lookingForCaseBracket = false; // not looking now } continue; } lookingForCaseBracket = false; // no opening bracket, don't indent if (line.charAt(i) == '}') // if close bracket { sw.setSwitchBracketCount(sw.getSwitchBracketCount() - 1); if (sw.getSwitchBracketCount() == 0) // if end of switch // statement { switchDepth--; // one less switch sw = swVector.pop(); // restore sw struct && remove last // entry from stack } continue; } // look for case or default header if (isPotentialKeyword && (findKeyword(line, i, "case") || findKeyword(line, i, "default"))) { if (sw.isUnindentCase()) // if unindented last case { sw.setUnindentCase(false); // stop unindenting previous case sw.setUnindentDepth(sw.getUnindentDepth() - 1); // reduce // depth } boolean isInQuote = false; char quoteChar = ' '; for (; i < lineLength; i++) // find colon { if (isInQuote) { if (line.charAt(i) == '\\') { i++; // bypass next char continue; } else if (line.charAt(i) == quoteChar) // check ending // quote { isInQuote = false; quoteChar = ' '; continue; } else { continue; // must close quote before continuing } } if (line.charAt(i) == '\'' || line.charAt(i) == '\"') // check // opening // quote { isInQuote = true; quoteChar = line.charAt(i); continue; } if (line.charAt(i) == ':') { if (i + 1 < lineLength && line.charAt(i + 1) == ':') { i++; // bypass scope resolution operator } else { break; // found it } } } i++; for (; i < lineLength; i++) // bypass whitespace { if (!Character.isWhitespace(line.charAt(i))) { break; } } if (i < lineLength) // check for bracket { if (line.charAt(i) == '{') // if bracket found { sw.setSwitchBracketCount(sw.getSwitchBracketCount() + 1); unindentNextLine = true; // start unindenting on next // line continue; } } lookingForCaseBracket = true; // bracket must be on next line i--; // need to check for comments continue; } if (isPotentialKeyword) { String name = getCurrentWord(line, i); // bypass the entire name i += name.length() - 1; } } // end of for loop if (isInEventTable) // if need to indent { indentLine(line, 1); // do it } if (sw.getUnindentDepth() > 0) // if need to unindent { unindentLine(line, sw.getUnindentDepth()); // do it } } /** * indent a line by a given number of tabsets by inserting leading * whitespace to the line argument. * * @param line a pointer to the line to indent. * @param unindent the number of tabsets to insert. * @return the number of characters inserted. */ int indentLine(StringBuilder line, int indent) { if (line.length() == 0 && !emptyLineFill) { return 0; } int charsToInsert; // number of chars to insert if (useTabs) // if formatted with tabs { charsToInsert = indent; // tabs to insert line.insert(0, ASUtils.repeat(charsToInsert, '\t')); // insert // the // tabs } else { charsToInsert = indent * indentLength; // compute chars to insert line.insert(0, ASUtils.repeat(charsToInsert, ' ')); // insert // the // spaces } return charsToInsert; } /** * unindent a line by a given number of tabsets by erasing the leading * whitespace from the line argument. * * @param line a pointer to the line to unindent. * @param unindent the number of tabsets to erase. * @return the number of characters erased. */ int unindentLine(StringBuilder line, int unindent) { int whitespace = ASUtils.findFirstNotOf(line, " \t", 0); if (whitespace == -1) // if line is blank { whitespace = line.length(); // must remove padding, if any } if (whitespace == 0) { return 0; } int charsToErase; // number of chars to erase if (useTabs) // if formatted with tabs { charsToErase = unindent; // tabs to erase if (charsToErase <= whitespace) // if there is enough whitespace { line.delete(0, charsToErase); // erase the tabs } else { charsToErase = 0; } } else { charsToErase = unindent * indentLength; // compute chars to erase if (charsToErase <= whitespace) // if there is enough whitespace { line.delete(0, charsToErase); // erase the spaces } else { charsToErase = 0; } } return charsToErase; } }