package org.antlr.runtime.debug;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.tree.BaseTree;
import org.antlr.runtime.tree.Tree;
import java.io.*;
import java.net.ConnectException;
import java.net.Socket;
import java.util.StringTokenizer;
public class RemoteDebugEventSocketListener implements Runnable {
static final int MAX_EVENT_ELEMENTS = 8;
DebugEventListener listener;
String machine;
int port;
Socket channel = null;
PrintWriter out;
BufferedReader in;
String event;
public String version;
public String grammarFileName;
int previousTokenIndex = -1;
boolean tokenIndexesInvalid = false;
public static class ProxyToken implements Token {
int index;
int type;
int channel;
int line;
int charPos;
String text;
public ProxyToken(int index) { this.index = index; }
public ProxyToken(int index, int type, int channel,
int line, int charPos, String text)
{
this.index = index;
this.type = type;
this.channel = channel;
this.line = line;
this.charPos = charPos;
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public void setText(String text) {
this.text = text;
}
@Override
public int getType() {
return type;
}
@Override
public void setType(int ttype) {
this.type = ttype;
}
@Override
public int getLine() {
return line;
}
@Override
public void setLine(int line) {
this.line = line;
}
@Override
public int getCharPositionInLine() {
return charPos;
}
@Override
public void setCharPositionInLine(int pos) {
this.charPos = pos;
}
@Override
public int getChannel() {
return channel;
}
@Override
public void setChannel(int channel) {
this.channel = channel;
}
@Override
public int getTokenIndex() {
return index;
}
@Override
public void setTokenIndex(int index) {
this.index = index;
}
@Override
public CharStream getInputStream() {
return null;
}
@Override
public void setInputStream(CharStream input) {
}
@Override
public String toString() {
String channelStr = "";
if ( channel!=Token.DEFAULT_CHANNEL ) {
channelStr=",channel="+channel;
}
return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]";
}
}
public static class ProxyTree extends BaseTree {
public int ID;
public int type;
public int line = 0;
public int charPos = -1;
public int tokenIndex = -1;
public String text;
public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) {
this.ID = ID;
this.type = type;
this.line = line;
this.charPos = charPos;
this.tokenIndex = tokenIndex;
this.text = text;
}
public ProxyTree(int ID) { this.ID = ID; }
@Override public int getTokenStartIndex() { return tokenIndex; }
@Override public void setTokenStartIndex(int index) { }
@Override public int getTokenStopIndex() { return 0; }
@Override public void setTokenStopIndex(int index) { }
@Override public Tree dupNode() { return null; }
@Override public int getType() { return type; }
@Override public String getText() { return text; }
@Override public String toString() {
return "fix this";
}
}
public RemoteDebugEventSocketListener(DebugEventListener listener,
String machine,
int port) throws IOException
{
this.listener = listener;
this.machine = machine;
this.port = port;
if( !openConnection() ) {
throw new ConnectException();
}
}
protected void eventHandler() {
try {
handshake();
event = in.readLine();
while ( event!=null ) {
dispatch(event);
ack();
event = in.readLine();
}
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace(System.err);
}
finally {
closeConnection();
}
}
protected boolean openConnection() {
boolean success = false;
try {
channel = new Socket(machine, port);
channel.setTcpNoDelay(true);
OutputStream os = channel.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
out = new PrintWriter(new BufferedWriter(osw));
InputStream is = channel.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF8");
in = new BufferedReader(isr);
success = true;
} catch(Exception e) {
System.err.println(e);
}
return success;
}
protected void closeConnection() {
try {
in.close(); in = null;
out.close(); out = null;
channel.close(); channel=null;
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace(System.err);
}
finally {
if ( in!=null ) {
try {in.close();} catch (IOException ioe) {
System.err.println(ioe);
}
}
if ( out!=null ) {
out.close();
}
if ( channel!=null ) {
try {channel.close();} catch (IOException ioe) {
System.err.println(ioe);
}
}
}
}
protected void handshake() throws IOException {
String antlrLine = in.readLine();
String[] antlrElements = getEventElements(antlrLine);
version = antlrElements[1];
String grammarLine = in.readLine();
String[] grammarElements = getEventElements(grammarLine);
grammarFileName = grammarElements[1];
ack();
listener.commence();
}
protected void ack() {
out.println("ack");
out.flush();
}
protected void dispatch(String line) {
String[] elements = getEventElements(line);
if ( elements==null || elements[0]==null ) {
System.err.println("unknown debug event: "+line);
return;
}
if ( elements[0].equals("enterRule") ) {
listener.enterRule(elements[1], elements[2]);
}
else if ( elements[0].equals("exitRule") ) {
listener.exitRule(elements[1], elements[2]);
}
else if ( elements[0].equals("enterAlt") ) {
listener.enterAlt(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("enterSubRule") ) {
listener.enterSubRule(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("exitSubRule") ) {
listener.exitSubRule(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("enterDecision") ) {
listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true"));
}
else if ( elements[0].equals("exitDecision") ) {
listener.exitDecision(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("location") ) {
listener.location(Integer.parseInt(elements[1]),
Integer.parseInt(elements[2]));
}
else if ( elements[0].equals("consumeToken") ) {
ProxyToken t = deserializeToken(elements, 1);
if ( t.getTokenIndex() == previousTokenIndex ) {
tokenIndexesInvalid = true;
}
previousTokenIndex = t.getTokenIndex();
listener.consumeToken(t);
}
else if ( elements[0].equals("consumeHiddenToken") ) {
ProxyToken t = deserializeToken(elements, 1);
if ( t.getTokenIndex() == previousTokenIndex ) {
tokenIndexesInvalid = true;
}
previousTokenIndex = t.getTokenIndex();
listener.consumeHiddenToken(t);
}
else if ( elements[0].equals("LT") ) {
Token t = deserializeToken(elements, 2);
listener.LT(Integer.parseInt(elements[1]), t);
}
else if ( elements[0].equals("mark") ) {
listener.mark(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("rewind") ) {
if ( elements[1]!=null ) {
listener.rewind(Integer.parseInt(elements[1]));
}
else {
listener.rewind();
}
}
else if ( elements[0].equals("beginBacktrack") ) {
listener.beginBacktrack(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("endBacktrack") ) {
int level = Integer.parseInt(elements[1]);
int successI = Integer.parseInt(elements[2]);
listener.endBacktrack(level, successI==DebugEventListener.TRUE);
}
else if ( elements[0].equals("exception") ) {
String excName = elements[1];
String indexS = elements[2];
String lineS = elements[3];
String posS = elements[4];
Class<? extends RecognitionException> excClass;
try {
excClass = Class.forName(excName).asSubclass(RecognitionException.class);
RecognitionException e = excClass.newInstance();
e.index = Integer.parseInt(indexS);
e.line = Integer.parseInt(lineS);
e.charPositionInLine = Integer.parseInt(posS);
listener.recognitionException(e);
}
catch (ClassNotFoundException cnfe) {
System.err.println("can't find class "+cnfe);
cnfe.printStackTrace(System.err);
}
catch (InstantiationException ie) {
System.err.println("can't instantiate class "+ie);
ie.printStackTrace(System.err);
}
catch (IllegalAccessException iae) {
System.err.println("can't access class "+iae);
iae.printStackTrace(System.err);
}
}
else if ( elements[0].equals("beginResync") ) {
listener.beginResync();
}
else if ( elements[0].equals("endResync") ) {
listener.endResync();
}
else if ( elements[0].equals("terminate") ) {
listener.terminate();
}
else if ( elements[0].equals("semanticPredicate") ) {
Boolean result = Boolean.valueOf(elements[1]);
String predicateText = elements[2];
predicateText = unEscapeNewlines(predicateText);
listener.semanticPredicate(result,
predicateText);
}
else if ( elements[0].equals("consumeNode") ) {
ProxyTree node = deserializeNode(elements, 1);
listener.consumeNode(node);
}
else if ( elements[0].equals("LN") ) {
int i = Integer.parseInt(elements[1]);
ProxyTree node = deserializeNode(elements, 2);
listener.LT(i, node);
}
else if ( elements[0].equals("createNodeFromTokenElements") ) {
int ID = Integer.parseInt(elements[1]);
int type = Integer.parseInt(elements[2]);
String text = elements[3];
text = unEscapeNewlines(text);
ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
listener.createNode(node);
}
else if ( elements[0].equals("createNode") ) {
int ID = Integer.parseInt(elements[1]);
int tokenIndex = Integer.parseInt(elements[2]);
ProxyTree node = new ProxyTree(ID);
ProxyToken token = new ProxyToken(tokenIndex);
listener.createNode(node, token);
}
else if ( elements[0].equals("nilNode") ) {
int ID = Integer.parseInt(elements[1]);
ProxyTree node = new ProxyTree(ID);
listener.nilNode(node);
}
else if ( elements[0].equals("errorNode") ) {
int ID = Integer.parseInt(elements[1]);
int type = Integer.parseInt(elements[2]);
String text = elements[3];
text = unEscapeNewlines(text);
ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
listener.errorNode(node);
}
else if ( elements[0].equals("becomeRoot") ) {
int newRootID = Integer.parseInt(elements[1]);
int oldRootID = Integer.parseInt(elements[2]);
ProxyTree newRoot = new ProxyTree(newRootID);
ProxyTree oldRoot = new ProxyTree(oldRootID);
listener.becomeRoot(newRoot, oldRoot);
}
else if ( elements[0].equals("addChild") ) {
int rootID = Integer.parseInt(elements[1]);
int childID = Integer.parseInt(elements[2]);
ProxyTree root = new ProxyTree(rootID);
ProxyTree child = new ProxyTree(childID);
listener.addChild(root, child);
}
else if ( elements[0].equals("setTokenBoundaries") ) {
int ID = Integer.parseInt(elements[1]);
ProxyTree node = new ProxyTree(ID);
listener.setTokenBoundaries(
node,
Integer.parseInt(elements[2]),
Integer.parseInt(elements[3]));
}
else {
System.err.println("unknown debug event: "+line);
}
}
protected ProxyTree deserializeNode(String[] elements, int offset) {
int ID = Integer.parseInt(elements[offset+0]);
int type = Integer.parseInt(elements[offset+1]);
int tokenLine = Integer.parseInt(elements[offset+2]);
int charPositionInLine = Integer.parseInt(elements[offset+3]);
int tokenIndex = Integer.parseInt(elements[offset+4]);
String text = elements[offset+5];
text = unEscapeNewlines(text);
return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text);
}
protected ProxyToken deserializeToken(String[] elements,
int offset)
{
String indexS = elements[offset+0];
String typeS = elements[offset+1];
String channelS = elements[offset+2];
String lineS = elements[offset+3];
String posS = elements[offset+4];
String text = elements[offset+5];
text = unEscapeNewlines(text);
int index = Integer.parseInt(indexS);
ProxyToken t =
new ProxyToken(index,
Integer.parseInt(typeS),
Integer.parseInt(channelS),
Integer.parseInt(lineS),
Integer.parseInt(posS),
text);
return t;
}
public void start() {
Thread t = new Thread(this);
t.start();
}
@Override
public void run() {
eventHandler();
}
public String[] getEventElements(String event) {
if ( event==null ) {
return null;
}
String[] elements = new String[MAX_EVENT_ELEMENTS];
String str = null;
try {
int firstQuoteIndex = event.indexOf('"');
if ( firstQuoteIndex>=0 ) {
String eventWithoutString = event.substring(0,firstQuoteIndex);
str = event.substring(firstQuoteIndex+1,event.length());
event = eventWithoutString;
}
StringTokenizer st = new StringTokenizer(event, "\t", false);
int i = 0;
while ( st.hasMoreTokens() ) {
if ( i>=MAX_EVENT_ELEMENTS ) {
return elements;
}
elements[i] = st.nextToken();
i++;
}
if ( str!=null ) {
elements[i] = str;
}
}
catch (Exception e) {
e.printStackTrace(System.err);
}
return elements;
}
protected String unEscapeNewlines(String txt) {
txt = txt.replaceAll("%0A","\n");
txt = txt.replaceAll("%0D","\r");
txt = txt.replaceAll("%25","%");
return txt;
}
public boolean tokenIndexesAreInvalid() {
return false;
}
}