package decompsource.com.github.abrarsyed.jastyle;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
public class OptParser
{
public final ASFormatter formatter;
/**
* @param formatter The formatter to which the parsed options will apply. Options are parsed and applied immediately.
*/
public OptParser(ASFormatter formatter)
{
this.formatter = formatter;
}
/**
* A method for no other reason than to help throw the exception.
* This is used because its helps cut down boilerplate.
* it is is used instead of chaining return statements for booleans, and many unnecessary ifs.
* Options are parsed and applied immediately.
*
* @throws MalformedOptionException
*/
private static void error() throws MalformedOptionException
{
throw new MalformedOptionException();
}
/*
* TODO: MAKE THIS THE ACTUAL JAVADOC OF THE FILE.
* Read jAstyle options from a file <br>
* Read a file form the file system, it skips the lines that start with #<br>
* <p/>
* <pre>
* A default options file may be used to set your favorite source style options.
* The command line options have precedence. If there is a conflict between a command line option and an option
* in the default options file, the command line option will be used.
* Artistic Style looks for this file in the following locations (in order):
* the file indicated by the --options= command line option;
* the file and directory indicated by the environment variable ARTISTIC_STYLE_OPTIONS (if it exists);
* This option file lookup can be disabled by specifying --options=none on the command line.
* Options may be set apart by new-lines, tabs, commas, or spaces.
* Long options in the options file may be written without the preceding '--'.
* Lines within the options file that begin with '#' are considered line-comments.
* Example of a default options file:
* <em>
* # this line is a comment
* --brackets=attach # this is a line-end comment
* # long options can be written without the preceding '--'
* indent-switches # cannot do this on the command line
* # short options must have the preceding '-'
* -t -p
* # short options can be concatenated together
* -M65Ucv
* </em>
* </pre>
* @param filename The name of the file that will be readed
* @return
* @throws IOException
*/
/**
* Parses an Astyle Options file.
* Unsupported, illegal, or malformed options will be logged and ignored.
* Options are parsed and applied immediately.
*
* @param file Options file to parse. Extension is irrelevant.
* @param log A logger to output stuff to. if this is null, the Global logger will be used.
* @return A list of all the errored lines. Empty list if there were no errored lines. NULL if something went wrong reading the file.
*/
public ArrayList<String> parseOptionFile(File file)
{
try
{
ArrayList<String> errors = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
while (line != null)
{
// comment or empty.
if (!(line.isEmpty() || line.startsWith("#")))
{
try
{
parseOption(line.trim());
}
catch (MalformedOptionException ex)
{
errors.add(line);
}
}
line = reader.readLine();
}
reader.close();
return errors;
}
catch (Exception e)
{
// error? return null.
return null;
}
}
/**
* @param opt This string is assumed to be starting with one or two hyphens.
*/
public void parseOption(final String opt) throws MalformedOptionException
{
// long option
if (opt.startsWith("--"))
{
parseLongOption(opt.replaceFirst("[-]{2}", ""));
}
// only 1? short option
else if (opt.startsWith("-"))
{
parseShortOption(opt.replaceFirst("[-]", ""));
}
// must be a long-option from the file.
// if its a file path from the command line, itl be handled outside here, after this returns false.
else
{
parseLongOption(opt);
}
}
private void parseLongOption(String opt) throws MalformedOptionException
{
if (opt.startsWith("--"))
{
throw new IllegalArgumentException("Trying to parse long option " + opt + " while it still cotnains a -");
}
String temp;
// Style checking
if (opt.startsWith("style="))
{
// 6 = length of "style="
temp = opt.substring(6);
temp = temp.toUpperCase();
temp = temp.replace("&", "");
temp = temp.replace("/", "");
try
{
formatter.setFormattingStyle(EnumFormatStyle.valueOf(temp));
}
catch (Exception e)
{
// no possible style. fail.
error();
}
}
// indent checking
else if (opt.startsWith("indent="))
{
temp = opt.substring(7);
// spaces.
if (temp.startsWith("spaces"))
{
formatter.setSpaceIndentation(getLongOptNum(temp, 4));
}
// tabs.
else if (temp.startsWith("tab"))
{
formatter.setTabIndentation(getLongOptNum(temp, 4), false);
}
else if (temp.startsWith("force-tab"))
{
formatter.setTabIndentation(getLongOptNum(temp, 4), true);
}
}
// indent type checking
else if (opt.startsWith("indent-"))
{
temp = opt.substring(7);
if (temp.equals("classes"))
{
formatter.setClassIndent(true);
}
else if (temp.equals("switches"))
{
formatter.setSwitchIndent(true);
}
else if (temp.equals("cases"))
{
formatter.setCaseIndent(true);
}
else if (temp.equals("blocks"))
{
formatter.setBlockIndent(true);
}
else if (temp.equals("brackets"))
{
formatter.setBracketIndent(true);
}
else if (temp.equals("namespaces"))
{
formatter.setNamespaceIndent(true);
}
else if (temp.equals("labels"))
{
formatter.setLabelIndent(true);
}
else if (temp.equals("preprocessor"))
{
formatter.setPreprocessorIndent(true);
}
}
else if (opt.startsWith("max-instatement-indent="))
{
formatter.setMaxInStatementIndentLength(getLongOptNum(opt, 40));
}
else if (opt.startsWith("min-conditional-indent="))
{
formatter.setMinConditionalIndentLength(getLongOptNum(opt, 8));
}
// bracket checking
else if (opt.startsWith("brackets="))
{
temp = opt.substring(9);
temp = temp.toUpperCase();
try
{
formatter.setBracketFormatMode(EnumBracketMode.valueOf(temp));
}
catch (Exception e)
{
// no possible Bracket Format mode. fail.
error();
}
}
// bracket checking
else if (opt.startsWith("break-"))
{
temp = opt.substring(6);
if (temp.startsWith("blocks"))
{
formatter.setBreakBlocksMode(true);
if (temp.contains("=all"))
{
formatter.setBreakClosingHeaderBlocksMode(true);
}
}
else if (temp.equals("closing-brackets"))
{
formatter.setBreakClosingHeaderBracketsMode(true);
}
else if (temp.equals("elseifs"))
{
formatter.setBreakElseIfsMode(true);
}
}
// padding stuff
else if (opt.startsWith("pad"))
{
temp = opt.substring(4);
if (temp.equals("oper"))
{
formatter.setOperatorPaddingMode(true);
}
else if (temp.startsWith("paren"))
{
temp = temp.substring(5);
if (temp.isEmpty())
{
formatter.setParensOutsidePaddingMode(true);
formatter.setParensInsidePaddingMode(true);
}
else if (temp.equals("out"))
{
formatter.setParensOutsidePaddingMode(true);
}
else if (temp.equals("in"))
{
formatter.setParensInsidePaddingMode(true);
}
else
{
error();
}
}
}
else if (opt.equals("unpad-paren"))
{
formatter.setParensUnPaddingMode(true);
}
// source mode stuff
else if (opt.startsWith("mode="))
{
// 6 = length of "style="
temp = opt.substring(5);
temp = temp.toUpperCase();
try
{
formatter.setSourceStyle(SourceMode.valueOf(temp));
}
catch (Exception e)
{
// no possible style. fail.
error();
}
}
// misc stuff.
else if (opt.equals("delete-empty-lines"))
{
formatter.setDeleteEmptyLinesMode(true);
}
else if (opt.startsWith("keep-one-line"))
{
temp = opt.substring(13);
if (temp.equals("statements"))
{
formatter.setSingleStatementsMode(false);
}
else if (temp.equals("blocks"))
{
formatter.setBreakOneLineBlocksMode(false);
}
else
{
error();
}
}
// suffix stuff
else if (opt.startsWith("suffix="))
{
temp = opt.substring(7);
if (temp.equals("none"))
{
formatter.setSuffix(null);
}
else if (!temp.isEmpty())
{
formatter.setSuffix(temp);
}
else
{
error();
}
}
else
{
error();
}
}
/**
* This method is used to parse short options. Short options should be no less than 1 and no more than 5 characters long.
*
* @param opt Should not start with a hyphen.
* @return
*/
private void parseShortOption(String opt) throws MalformedOptionException
{
if (opt.startsWith("-"))
{
throw new IllegalArgumentException("Trying to parse short option " + opt + " while it still cotnains a -");
}
char optStart = opt.charAt(0);
String start = "" + optStart;
int tempNum;
switch (optStart)
{
// style stuff
case 'A':
tempNum = getShortOptNum(start, opt, 0);
if (tempNum >= EnumFormatStyle.values().length || tempNum < 0)
{
error();
}
formatter.setFormattingStyle(EnumFormatStyle.values()[tempNum]);
break;
// spaces
case 's':
formatter.setSpaceIndentation(getShortOptNum(start, opt, 4));
break;
// tabs
case 't':
formatter.setTabIndentation(getShortOptNum(start, opt, 4), false);
break;
case 'T':
formatter.setTabIndentation(getShortOptNum(start, opt, 4), true);
break;
// brackets
case 'b':
formatter.setBracketFormatMode(EnumBracketMode.BREAK);
break;
case 'a':
formatter.setBracketFormatMode(EnumBracketMode.ATTACH);
break;
case 'l':
formatter.setBracketFormatMode(EnumBracketMode.LINUX);
break;
case 'u':
formatter.setBracketFormatMode(EnumBracketMode.STROUSTRUP);
break;
// other indent options
case 'C':
formatter.setClassIndent(true);
break;
case 'S':
formatter.setSwitchIndent(true);
break;
case 'K':
formatter.setCaseIndent(true);
break;
case 'G':
formatter.setBlockIndent(true);
break;
case 'B':
formatter.setBracketIndent(true);
break;
case 'N':
formatter.setNamespaceIndent(true);
break;
case 'L':
formatter.setLabelIndent(true);
break;
case 'w':
formatter.setPreprocessorIndent(true);
break;
case 'M':
formatter.setMaxInStatementIndentLength(getShortOptNum(start, opt, 40));
break;
case 'm':
formatter.setMinConditionalIndentLength(getShortOptNum(start, opt, 8));
break;
// "break" options
case 'f':
formatter.setBreakBlocksMode(true);
break;
case 'F':
formatter.setBreakBlocksMode(true);
formatter.setBreakClosingHeaderBlocksMode(true);
break;
case 'y':
formatter.setBreakClosingHeaderBracketsMode(true);
break;
case 'e':
formatter.setBreakElseIfsMode(true);
break;
// X options.
case 'x':
if (opt.length() == 1)
{
formatter.setDeleteEmptyLinesMode(true);
break;
}
// TODO: MORE OPTIONS STARTING IN X HERE
else
{
error();
}
// Parentheses and padding.
case 'p':
formatter.setOperatorPaddingMode(true);
break;
case 'P':
formatter.setParensOutsidePaddingMode(true);
formatter.setParensInsidePaddingMode(true);
break;
case 'd':
formatter.setParensOutsidePaddingMode(true);
break;
case 'D':
formatter.setParensInsidePaddingMode(true);
break;
case 'U':
formatter.setParensUnPaddingMode(true);
break;
// misc stuff.
case 'o':
formatter.setSingleStatementsMode(false);
break;
case 'O':
formatter.setBreakOneLineBlocksMode(false);
break;
// suffix
case 'n':
formatter.setSuffix(null);
break;
}
// nothing else we can parse? throw de exception.
error();
}
/**
* Parses a string matching pattern *=* and tries to convert the characters after the equal sign to an integer.
*
* @param str
* @param the default number to return if there is no equal sign.
* @return
* @throws MalformedOptionException
*/
private int getLongOptNum(String str, int def) throws MalformedOptionException
{
if (str.contains("="))
{
try
{
String[] split = str.split("=");
return Integer.parseInt(split[1]);
}
catch (Exception e)
{
error();
}
}
return def;
}
/**
* Parses a string matching pattern *# and tries to convert the # chars to a number given the * chars.
*
* @param start The section of the option occurring before the number.
* @param str The entire option.
* @param def The number to be returned if there is no number section.
* @return
* @throws MalformedOptionException
*/
private int getShortOptNum(String start, String str, int def) throws MalformedOptionException
{
if (!str.startsWith(start))
{
throw new IllegalArgumentException("Param start must be the portion of param str before the number!");
}
if (str.length() > start.length())
{
try
{
return Integer.parseInt(str.substring(start.length()));
}
catch (Exception e)
{
error();
}
}
return def;
}
}