package com.drawbridge.jsengine.ast;
import java.io.StringReader;
import java.util.HashSet;
import java.util.LinkedList;
import com.drawbridge.jsengine.Scope;
import com.drawbridge.jsengine.ast.Evaluator.EvaluatorException;
import com.drawbridge.jsengine.jsobjects.ExecutionException;
import com.drawbridge.utils.AnalyticUtils;
import com.drawbridge.utils.Utils;
import com.google.caja.lexer.CharProducer;
import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.InputSource;
import com.google.caja.lexer.JsLexer;
import com.google.caja.lexer.JsTokenQueue;
import com.google.caja.lexer.JsTokenType;
import com.google.caja.lexer.ParseException;
import com.google.caja.lexer.Token;
import com.google.caja.parser.AbstractParseTreeNode;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.js.Statement;
import com.google.caja.reporting.DevNullMessageQueue;
import com.google.caja.reporting.MessagePart;
import com.google.caja.util.Criterion;
public class DBParser
{
private Scope mScope;
private LinkedList<ParserListener> mParserListeners = new LinkedList<ParserListener>();
private LinkedList<ParseTreeNode> mStatements = new LinkedList<ParseTreeNode>();
private LinkedList<Evaluator> mEvaluators = new LinkedList<Evaluator>();
private ParserListener mSourceListener;
private String mSnapshot;
private LinkedList<String> mComments = new LinkedList<String>();
private static DBParser mInstance = null;
public int mNumberOfParses = 0;
public HashSet<Evaluator> mRecentlyChangedEvaluators = new HashSet<Evaluator>();
private boolean mVerboseOutput = true;
public static boolean hasInstance()
{
return (mInstance != null);
}
public static DBParser getInstance()
{
if (mInstance == null)
mInstance = new DBParser();
return mInstance;
}
private DBParser() {
}
public LinkedList<ParserListener> getParserListeners() //**onParseChange!
{
return mParserListeners;
}
public void setParams(ParserListener sourceListener, LinkedList<ParserListener> listeners, Scope scope, String input)
{
Utils.out.println(this.getClass(), "Set Parser Params");
mStatements.clear();
mParserListeners.clear();
mSnapshot = input;
mSourceListener = sourceListener;
mScope = scope;
mParserListeners.addAll(listeners);
mComments = new LinkedList<String>();
}
public void parse()
{
if(mRecentlyChangedEvaluators != null)
mRecentlyChangedEvaluators.clear();
mNumberOfParses++;
CharProducer cp = CharProducer.Factory.create(new StringReader(mSnapshot), InputSource.PREDEFINED);
JsLexer lexer = new JsLexer(cp);
JsTokenQueue tokenQueue = new JsTokenQueue(lexer, InputSource.PREDEFINED, new Criterion<Token<JsTokenType>>()
{
public boolean accept(Token<JsTokenType> t)
{
if (t.type == JsTokenType.COMMENT)
{
FilePosition filePos = t.pos;
while (mComments.size() <= filePos.endLineNo() - 1)
mComments.add("");
mComments.set(filePos.endLineNo() - 1, t.text);
}
return JsTokenType.COMMENT != t.type
&& JsTokenType.LINE_CONTINUATION != t.type;
}
});
com.google.caja.parser.js.Parser parser = new com.google.caja.parser.js.Parser(tokenQueue, DevNullMessageQueue.singleton());
parser.setRecoverFromFailure(false);
ParseTreeNode result = null;
boolean exception = false;
int lastLinePosition = 0;
mComments = new LinkedList<String>();
String[] inputLines = mSnapshot.split("\n");
int evaluatorIndex = 0;
try
{
Utils.out.println(getClass(), "Parsing");
while (!tokenQueue.isEmpty())
{
lastLinePosition = tokenQueue.currentPosition().endLineNo() - 1;
result = parser.parseStatement();
mStatements.add(result);
Evaluator newEvaluator = Evaluator.getEvaluator(null, mScope, result);
if (evaluatorIndex < mEvaluators.size())
{
if (DBParser.areEvaluatorsEqual(newEvaluator, mEvaluators.get(evaluatorIndex)))
{
// Don't replace
Utils.out.println(getClass(), "Not replacing eval due to equality: " + newEvaluator.getClass().getSimpleName());
// If it's an integer, we'd like to record the change for dialling
if(newEvaluator instanceof IntegerLiteralEvaluator)
mRecentlyChangedEvaluators.add( (IntegerLiteralEvaluator) newEvaluator);
}
else
{
recordRecentlyChangedEvaluator(mEvaluators.get(evaluatorIndex), newEvaluator);
mEvaluators.set(evaluatorIndex, newEvaluator);
}
}
else
{
mEvaluators.add(newEvaluator);
}
evaluatorIndex++;
// Store the Comments
while (mComments.size() <= result.getFilePosition().endLineNo() - 1)
{
mComments.add("");
}
String inputLine = inputLines[result.getFilePosition().endLineNo() - 1];
if (mVerboseOutput)
Utils.out.println(this.getClass(), ((AbstractParseTreeNode) result).toStringDeep());
mComments.set(result.getFilePosition().endLineNo() - 1, inputLine.substring(result.getFilePosition().endCharInLine() - 1));
}
// Remove extra old evaluators
int evalSize = mEvaluators.size();
for (int i = evaluatorIndex; i < evalSize && i < mEvaluators.size(); i++)
mEvaluators.remove(i);
} catch (ParseException e)
{
exception = true;
Utils.err.println(getClass(), "Parse Exception");
MessagePart mp = e.getCajaMessage().getMessageParts().get(0);
if (mp instanceof FilePosition)
{
notifyListenersOfException(((FilePosition) mp).endLineNo() - 1, e.getMessage());
}
else
{
notifyListenersOfException(lastLinePosition, e.getMessage());
}
}
if (!exception)
notifyListenersOfChange();
}
public String getSnapshot()
{
return mSnapshot;
}
/**
* Returns the positions of the last bit of recognized text in the snapshot
*
* @return LinkedList<String>
*/
public LinkedList<String> getComments()
{
return mComments;
}
public Scope getScope()
{
return mScope;
}
public void notifyOfEvalChange(ParserListener listener)
{
mSourceListener = listener;
notifyListenersOfChange();
}
private void notifyListenersOfChange()
{
// Utils.out.println(getClass(), "Notifying listeners:" + mParserListeners.size());
for (ParserListener listener : mParserListeners)
{
if (!listener.equals(mSourceListener))
listener.onParserChange();
}
}
private void notifyListenersOfException(int lineNumber, String message)
{
AnalyticUtils.recordSyntaxError(message, lineNumber);
for (ParserListener listener : mParserListeners)
{
listener.onParserException(lineNumber, message);
}
}
public void addParserListener(ParserListener newListener)
{
mParserListeners.add(newListener);
}
/**
* Executes the E-AST
*
* @return
* @throws EvaluatorException
*/
public Object evaluate() throws EvaluatorException
{
if (mScope == null)
throw new RuntimeException("SCOPE IS NULL");
if (mEvaluators == null)
throw new RuntimeException("Cannot evaluate a null parser output");
for (int i = 0; i < mEvaluators.size(); i++)
{
try
{
mEvaluators.get(i).evaluate();
} catch (ExecutionException e)
{
Utils.err.println("Error on line:" + (mEvaluators.get(i).getFilePosition().startLineNo() - 1));
e.printStackTrace();
this.notifyListenersOfException(mEvaluators.get(i).getFilePosition().startLineNo() - 1, e.getMessage());
}
}
return null;
}
public String getASTString()
{
String result = "";
for (int i = 0; i < mStatements.size(); i++)
{
result += "\n" + ((Statement) mStatements.get(i)).toString();
}
return result;
}
public LinkedList<Evaluator> getEvaluators()
{
return mEvaluators;
}
public void printScope()
{
try
{
mScope.printScopeDeep(0);
} catch (Exception e)
{
e.printStackTrace();
}
}
public void setSnapshot(String snapshot)
{
mSnapshot = snapshot;
}
public boolean containsEvaluator(Evaluator e)
{
return containsEvaluator(mEvaluators, e);
}
private boolean containsEvaluator(LinkedList<Evaluator> evals, Evaluator e)
{
for(int i = 0; i < evals.size(); i++)
{
if(evals.get(i) == e)
return true;
}
for (Evaluator child : evals)
{
if (containsEvaluator(child.mChildren, e))
return true;
}
return false;
}
public static boolean areEvaluatorsEqual(Evaluator a, Evaluator b)
{
// Check type and children
if (a != null & b != null)
{
if (a.getClass().equals(b.getClass()))
{
if (a.mChildren.size() == b.mChildren.size())
{
for (int i = 0; i < a.mChildren.size(); i++)
{
if (!areEvaluatorsEqual(a.mChildren.get(i), b.mChildren.get(i)))
return false;
}
}
}
if (a == b && a.getFilePosition().equals(b.getFilePosition()))
return true;
else
return false;
}
else
return false;
}
private void recordRecentlyChangedEvaluator(Evaluator old, Evaluator recent)
{
// Check type and children
if (old != null && recent != null && !old.compare(recent))
{
for (int i = 0; i < Math.max(old.mChildren.size(), recent.mChildren.size()); i++)
{
if (i >= old.mChildren.size())
{
if(recent instanceof IntegerLiteralEvaluator)
mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent);
}
else if (i < old.mChildren.size() && i < recent.mChildren.size())
{
recordRecentlyChangedEvaluator(old.mChildren.get(i), recent.mChildren.get(i));
}
}
if (old.mChildren.size() == 0 && recent.mChildren.size() == 0)
{
if(recent instanceof IntegerLiteralEvaluator)
mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent);
}
}
else if (old == null && recent != null)
{
if(recent instanceof IntegerLiteralEvaluator)
mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent);
}
if(recent instanceof IntegerLiteralEvaluator && recent != null)
mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent);
}
public void setVerboseOutput(boolean b)
{
mVerboseOutput = b;
}
}