package com.coverity.ps.sac.parser.as;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.coverity.ps.sac.io.CoveritySaFormatter;
import com.coverity.ps.sac.parser.Parser;
/**
* ActionScript 3.0 language parser that detects classes and methods.
* @author rhollines
*/
public class ActionScriptParser implements Parser {
static int parseCount = 0;
private List<ActionScriptScanner.Token> tokens;
private int index = 0;
ActionScriptScanner.Token currentToken;
Map<Integer, String> functions = new HashMap<Integer, String>();
private ActionScriptScanner scanner;
private StringBuffer functionMetrics;
private String filename;
private int functionCount = 0;
/**
* Default Constructor
*
*/
public ActionScriptParser() {
}
/**
* Initializes the parser class
*
* @param filename file to parse
*/
private void initialize(String filename) throws IOException {
this.filename = filename;
BufferedReader reader = null;
FileWriter inclFile = null;
FileWriter sourceFile = null;
try {
// read file
StringBuilder input = new StringBuilder();
reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
while (line != null) {
input.append(line);
input.append('\n');
line = reader.readLine();
}
File outputDirectory = new File("output/intdir");
outputDirectory.mkdir();
outputDirectory = new File(CoveritySaFormatter.OUTPUT_PATH);
outputDirectory.mkdir();
outputDirectory = new File(CoveritySaFormatter.OUTPUT_PATH
+ "/emit");
outputDirectory.mkdir();
outputDirectory = new File(CoveritySaFormatter.OUTPUT_PATH
+ "/output");
outputDirectory.mkdir();
outputDirectory = new File(CoveritySaFormatter.OUTPUT_PATH
+ "/emit/f" + (parseCount++));
outputDirectory.mkdir();
// create incl file
inclFile = new FileWriter(outputDirectory.toString() + "/incl");
inclFile.write(filename + '|');
// create source file
final String source = input.toString();
sourceFile = new FileWriter(outputDirectory.toString() + "/source");
sourceFile.write(source);
// scan source
this.scanner = new ActionScriptScanner(source);
tokens = this.scanner.scan();
nextToken();
} finally {
try {
if (reader != null) {
reader.close();
}
if (inclFile != null) {
inclFile.close();
}
if (sourceFile != null) {
sourceFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
if (args.length > 0) {
ActionScriptParser parser = new ActionScriptParser();
parser.parse(args[0], new StringBuffer());
}
}
/**
* Get next token
*/
private void nextToken() {
if (index < tokens.size()) {
currentToken = (ActionScriptScanner.Token) tokens.get(index++);
} else {
currentToken = new ActionScriptScanner.Token(-1,
ActionScriptScanner.Token.Type.EOS);
}
}
/**
* Get next token
*
* @param i position
*/
private ActionScriptScanner.Token getToken(int i) {
if (i < tokens.size()) {
return tokens.get(i);
}
return new ActionScriptScanner.Token(-1,
ActionScriptScanner.Token.Type.EOS);
}
private boolean match(ActionScriptScanner.Token.Type type) {
return type == currentToken.getType();
}
private ActionScriptScanner.Token.Type getTokenType() {
return currentToken.getType();
}
private int getTokenLineNumber() {
return currentToken.getLineNumber();
}
private String getTokenValue() {
return currentToken.getValue();
}
public String getFunction(int lineNumber) {
String function = this.functions.get(lineNumber);
if (function != null) {
return function;
}
return "unknown";
}
public int getFunctionCount() {
return this.functionCount;
}
public int getLineCount() {
return this.scanner.getLineCount();
}
/**
* Parses ActionScript file
* @throws IOException
*/
public boolean parse(String filename, StringBuffer functionMetrics) throws IOException {
initialize(filename);
this.functionMetrics = functionMetrics;
StringBuilder packageName = new StringBuilder();
String className = "";
while (!match(ActionScriptScanner.Token.Type.EOS)) {
if (match(ActionScriptScanner.Token.Type.PACKAGE)) {
nextToken();
while (match(ActionScriptScanner.Token.Type.IDENT)) {
packageName.append(currentToken.getValue());
nextToken();
if (match(ActionScriptScanner.Token.Type.DOT)) {
packageName.append('.');
nextToken();
}
}
}
if (match(ActionScriptScanner.Token.Type.CLASS)) {
nextToken();
className = currentToken.getValue();
}
// found a function
if (match(ActionScriptScanner.Token.Type.FUNCTION)) {
int start = currentToken.getLineNumber();
nextToken();
// check to see if we have an anonymous function
final String functionName = currentToken.getValue() == "(" ? "unknown"
: (packageName.toString() + "." + className + "." + currentToken
.getValue());
this.functionCount ++;
// add function entry
this.functionMetrics.append("<fnmetric><file>");
this.functionMetrics.append(this.filename);
this.functionMetrics.append("</file><fnmet>");
this.functionMetrics.append(functionName);
this.functionMetrics.append("</fnmet></fnmetric>");
// find function end via curly braces
int state = -1;
do {
nextToken();
if (currentToken.getLineNumber() != start) {
functions.put(start, functionName);
start = currentToken.getLineNumber();
}
if (match(ActionScriptScanner.Token.Type.OCBR)) {
if (state < 0) {
state = 1;
} else {
state++;
}
}
if (match(ActionScriptScanner.Token.Type.CCBR)) {
state--;
}
} while (!match(ActionScriptScanner.Token.Type.EOS) && state != 0);
functions.put(start, functionName);
}
// default update
nextToken();
}
return true;
}
}