1/*
2 [The "BSD license"]
3 Copyright (c) 2005-2009 Terence Parr
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10     notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12     notice, this list of conditions and the following disclaimer in the
13     documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15     derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28package org.antlr.runtime.debug;
29
30import org.antlr.runtime.RecognitionException;
31import org.antlr.runtime.Token;
32import org.antlr.runtime.CharStream;
33import org.antlr.runtime.tree.BaseTree;
34import org.antlr.runtime.tree.Tree;
35
36import java.io.*;
37import java.net.ConnectException;
38import java.net.Socket;
39import java.util.StringTokenizer;
40
41public class RemoteDebugEventSocketListener implements Runnable {
42	static final int MAX_EVENT_ELEMENTS = 8;
43	DebugEventListener listener;
44	String machine;
45	int port;
46	Socket channel = null;
47	PrintWriter out;
48	BufferedReader in;
49	String event;
50	/** Version of ANTLR (dictates events) */
51	public String version;
52	public String grammarFileName;
53	/** Track the last token index we saw during a consume.  If same, then
54	 *  set a flag that we have a problem.
55	 */
56	int previousTokenIndex = -1;
57	boolean tokenIndexesInvalid = false;
58
59	public static class ProxyToken implements Token {
60		int index;
61		int type;
62		int channel;
63		int line;
64		int charPos;
65		String text;
66		public ProxyToken(int index) { this.index = index; }
67		public ProxyToken(int index, int type, int channel,
68						  int line, int charPos, String text)
69		{
70			this.index = index;
71			this.type = type;
72			this.channel = channel;
73			this.line = line;
74			this.charPos = charPos;
75			this.text = text;
76		}
77		public String getText() {
78			return text;
79		}
80		public void setText(String text) {
81			this.text = text;
82		}
83		public int getType() {
84			return type;
85		}
86		public void setType(int ttype) {
87			this.type = ttype;
88		}
89		public int getLine() {
90			return line;
91		}
92		public void setLine(int line) {
93			this.line = line;
94		}
95		public int getCharPositionInLine() {
96			return charPos;
97		}
98		public void setCharPositionInLine(int pos) {
99			this.charPos = pos;
100		}
101		public int getChannel() {
102			return channel;
103		}
104		public void setChannel(int channel) {
105			this.channel = channel;
106		}
107		public int getTokenIndex() {
108			return index;
109		}
110		public void setTokenIndex(int index) {
111			this.index = index;
112		}
113		public CharStream getInputStream() {
114			return null;
115		}
116		public void setInputStream(CharStream input) {
117		}
118		public String toString() {
119			String channelStr = "";
120			if ( channel!=Token.DEFAULT_CHANNEL ) {
121				channelStr=",channel="+channel;
122			}
123			return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]";
124		}
125	}
126
127	public static class ProxyTree extends BaseTree {
128		public int ID;
129		public int type;
130		public int line = 0;
131		public int charPos = -1;
132		public int tokenIndex = -1;
133		public String text;
134
135		public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) {
136			this.ID = ID;
137			this.type = type;
138			this.line = line;
139			this.charPos = charPos;
140			this.tokenIndex = tokenIndex;
141			this.text = text;
142		}
143
144		public ProxyTree(int ID) { this.ID = ID; }
145
146		public int getTokenStartIndex() { return tokenIndex; }
147		public void setTokenStartIndex(int index) {	}
148		public int getTokenStopIndex() { return 0; }
149		public void setTokenStopIndex(int index) { }
150		public Tree dupNode() {	return null; }
151		public int getType() { return type; }
152		public String getText() { return text; }
153		public String toString() {
154			return "fix this";
155		}
156	}
157
158	public RemoteDebugEventSocketListener(DebugEventListener listener,
159										  String machine,
160										  int port) throws IOException
161	{
162		this.listener = listener;
163		this.machine = machine;
164		this.port = port;
165
166        if( !openConnection() ) {
167            throw new ConnectException();
168        }
169	}
170
171	protected void eventHandler() {
172		try {
173			handshake();
174			event = in.readLine();
175			while ( event!=null ) {
176				dispatch(event);
177				ack();
178				event = in.readLine();
179			}
180		}
181		catch (Exception e) {
182			System.err.println(e);
183			e.printStackTrace(System.err);
184		}
185		finally {
186            closeConnection();
187		}
188	}
189
190    protected boolean openConnection() {
191        boolean success = false;
192        try {
193            channel = new Socket(machine, port);
194            channel.setTcpNoDelay(true);
195			OutputStream os = channel.getOutputStream();
196			OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
197			out = new PrintWriter(new BufferedWriter(osw));
198			InputStream is = channel.getInputStream();
199			InputStreamReader isr = new InputStreamReader(is, "UTF8");
200			in = new BufferedReader(isr);
201            success = true;
202        } catch(Exception e) {
203            System.err.println(e);
204        }
205        return success;
206    }
207
208    protected void closeConnection() {
209        try {
210            in.close(); in = null;
211            out.close(); out = null;
212            channel.close(); channel=null;
213        }
214        catch (Exception e) {
215            System.err.println(e);
216            e.printStackTrace(System.err);
217        }
218        finally {
219            if ( in!=null ) {
220                try {in.close();} catch (IOException ioe) {
221                    System.err.println(ioe);
222                }
223            }
224            if ( out!=null ) {
225                out.close();
226            }
227            if ( channel!=null ) {
228                try {channel.close();} catch (IOException ioe) {
229                    System.err.println(ioe);
230                }
231            }
232        }
233
234    }
235
236	protected void handshake() throws IOException {
237		String antlrLine = in.readLine();
238		String[] antlrElements = getEventElements(antlrLine);
239		version = antlrElements[1];
240		String grammarLine = in.readLine();
241		String[] grammarElements = getEventElements(grammarLine);
242		grammarFileName = grammarElements[1];
243		ack();
244		listener.commence(); // inform listener after handshake
245	}
246
247	protected void ack() {
248        out.println("ack");
249		out.flush();
250	}
251
252	protected void dispatch(String line) {
253        //System.out.println("event: "+line);
254        String[] elements = getEventElements(line);
255		if ( elements==null || elements[0]==null ) {
256			System.err.println("unknown debug event: "+line);
257			return;
258		}
259		if ( elements[0].equals("enterRule") ) {
260			listener.enterRule(elements[1], elements[2]);
261		}
262		else if ( elements[0].equals("exitRule") ) {
263			listener.exitRule(elements[1], elements[2]);
264		}
265		else if ( elements[0].equals("enterAlt") ) {
266			listener.enterAlt(Integer.parseInt(elements[1]));
267		}
268		else if ( elements[0].equals("enterSubRule") ) {
269			listener.enterSubRule(Integer.parseInt(elements[1]));
270		}
271		else if ( elements[0].equals("exitSubRule") ) {
272			listener.exitSubRule(Integer.parseInt(elements[1]));
273		}
274		else if ( elements[0].equals("enterDecision") ) {
275			listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true"));
276		}
277		else if ( elements[0].equals("exitDecision") ) {
278			listener.exitDecision(Integer.parseInt(elements[1]));
279		}
280		else if ( elements[0].equals("location") ) {
281			listener.location(Integer.parseInt(elements[1]),
282							  Integer.parseInt(elements[2]));
283		}
284		else if ( elements[0].equals("consumeToken") ) {
285			ProxyToken t = deserializeToken(elements, 1);
286			if ( t.getTokenIndex() == previousTokenIndex ) {
287				tokenIndexesInvalid = true;
288			}
289			previousTokenIndex = t.getTokenIndex();
290			listener.consumeToken(t);
291		}
292		else if ( elements[0].equals("consumeHiddenToken") ) {
293			ProxyToken t = deserializeToken(elements, 1);
294			if ( t.getTokenIndex() == previousTokenIndex ) {
295				tokenIndexesInvalid = true;
296			}
297			previousTokenIndex = t.getTokenIndex();
298			listener.consumeHiddenToken(t);
299		}
300		else if ( elements[0].equals("LT") ) {
301			Token t = deserializeToken(elements, 2);
302			listener.LT(Integer.parseInt(elements[1]), t);
303		}
304		else if ( elements[0].equals("mark") ) {
305			listener.mark(Integer.parseInt(elements[1]));
306		}
307		else if ( elements[0].equals("rewind") ) {
308			if ( elements[1]!=null ) {
309				listener.rewind(Integer.parseInt(elements[1]));
310			}
311			else {
312				listener.rewind();
313			}
314		}
315		else if ( elements[0].equals("beginBacktrack") ) {
316			listener.beginBacktrack(Integer.parseInt(elements[1]));
317		}
318		else if ( elements[0].equals("endBacktrack") ) {
319			int level = Integer.parseInt(elements[1]);
320			int successI = Integer.parseInt(elements[2]);
321			listener.endBacktrack(level, successI==DebugEventListener.TRUE);
322		}
323		else if ( elements[0].equals("exception") ) {
324			String excName = elements[1];
325			String indexS = elements[2];
326			String lineS = elements[3];
327			String posS = elements[4];
328			Class excClass = null;
329			try {
330				excClass = Class.forName(excName);
331				RecognitionException e =
332					(RecognitionException)excClass.newInstance();
333				e.index = Integer.parseInt(indexS);
334				e.line = Integer.parseInt(lineS);
335				e.charPositionInLine = Integer.parseInt(posS);
336				listener.recognitionException(e);
337			}
338			catch (ClassNotFoundException cnfe) {
339				System.err.println("can't find class "+cnfe);
340				cnfe.printStackTrace(System.err);
341			}
342			catch (InstantiationException ie) {
343				System.err.println("can't instantiate class "+ie);
344				ie.printStackTrace(System.err);
345			}
346			catch (IllegalAccessException iae) {
347				System.err.println("can't access class "+iae);
348				iae.printStackTrace(System.err);
349			}
350		}
351		else if ( elements[0].equals("beginResync") ) {
352			listener.beginResync();
353		}
354		else if ( elements[0].equals("endResync") ) {
355			listener.endResync();
356		}
357		else if ( elements[0].equals("terminate") ) {
358			listener.terminate();
359		}
360		else if ( elements[0].equals("semanticPredicate") ) {
361			Boolean result = Boolean.valueOf(elements[1]);
362			String predicateText = elements[2];
363			predicateText = unEscapeNewlines(predicateText);
364			listener.semanticPredicate(result.booleanValue(),
365									   predicateText);
366		}
367		else if ( elements[0].equals("consumeNode") ) {
368			ProxyTree node = deserializeNode(elements, 1);
369			listener.consumeNode(node);
370		}
371		else if ( elements[0].equals("LN") ) {
372			int i = Integer.parseInt(elements[1]);
373			ProxyTree node = deserializeNode(elements, 2);
374			listener.LT(i, node);
375		}
376		else if ( elements[0].equals("createNodeFromTokenElements") ) {
377			int ID = Integer.parseInt(elements[1]);
378			int type = Integer.parseInt(elements[2]);
379			String text = elements[3];
380			text = unEscapeNewlines(text);
381			ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
382			listener.createNode(node);
383		}
384		else if ( elements[0].equals("createNode") ) {
385			int ID = Integer.parseInt(elements[1]);
386			int tokenIndex = Integer.parseInt(elements[2]);
387			// create dummy node/token filled with ID, tokenIndex
388			ProxyTree node = new ProxyTree(ID);
389			ProxyToken token = new ProxyToken(tokenIndex);
390			listener.createNode(node, token);
391		}
392		else if ( elements[0].equals("nilNode") ) {
393			int ID = Integer.parseInt(elements[1]);
394			ProxyTree node = new ProxyTree(ID);
395			listener.nilNode(node);
396		}
397		else if ( elements[0].equals("errorNode") ) {
398			// TODO: do we need a special tree here?
399			int ID = Integer.parseInt(elements[1]);
400			int type = Integer.parseInt(elements[2]);
401			String text = elements[3];
402			text = unEscapeNewlines(text);
403			ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
404			listener.errorNode(node);
405		}
406		else if ( elements[0].equals("becomeRoot") ) {
407			int newRootID = Integer.parseInt(elements[1]);
408			int oldRootID = Integer.parseInt(elements[2]);
409			ProxyTree newRoot = new ProxyTree(newRootID);
410			ProxyTree oldRoot = new ProxyTree(oldRootID);
411			listener.becomeRoot(newRoot, oldRoot);
412		}
413		else if ( elements[0].equals("addChild") ) {
414			int rootID = Integer.parseInt(elements[1]);
415			int childID = Integer.parseInt(elements[2]);
416			ProxyTree root = new ProxyTree(rootID);
417			ProxyTree child = new ProxyTree(childID);
418			listener.addChild(root, child);
419		}
420		else if ( elements[0].equals("setTokenBoundaries") ) {
421			int ID = Integer.parseInt(elements[1]);
422			ProxyTree node = new ProxyTree(ID);
423			listener.setTokenBoundaries(
424				node,
425				Integer.parseInt(elements[2]),
426				Integer.parseInt(elements[3]));
427		}
428		else {
429			System.err.println("unknown debug event: "+line);
430		}
431	}
432
433	protected ProxyTree deserializeNode(String[] elements, int offset) {
434		int ID = Integer.parseInt(elements[offset+0]);
435		int type = Integer.parseInt(elements[offset+1]);
436		int tokenLine = Integer.parseInt(elements[offset+2]);
437		int charPositionInLine = Integer.parseInt(elements[offset+3]);
438		int tokenIndex = Integer.parseInt(elements[offset+4]);
439		String text = elements[offset+5];
440		text = unEscapeNewlines(text);
441		return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text);
442	}
443
444	protected ProxyToken deserializeToken(String[] elements,
445										  int offset)
446	{
447		String indexS = elements[offset+0];
448		String typeS = elements[offset+1];
449		String channelS = elements[offset+2];
450		String lineS = elements[offset+3];
451		String posS = elements[offset+4];
452		String text = elements[offset+5];
453		text = unEscapeNewlines(text);
454		int index = Integer.parseInt(indexS);
455		ProxyToken t =
456			new ProxyToken(index,
457						   Integer.parseInt(typeS),
458						   Integer.parseInt(channelS),
459						   Integer.parseInt(lineS),
460						   Integer.parseInt(posS),
461						   text);
462		return t;
463	}
464
465	/** Create a thread to listen to the remote running recognizer */
466	public void start() {
467		Thread t = new Thread(this);
468		t.start();
469	}
470
471	public void run() {
472		eventHandler();
473	}
474
475	// M i s c
476
477	public String[] getEventElements(String event) {
478		if ( event==null ) {
479			return null;
480		}
481		String[] elements = new String[MAX_EVENT_ELEMENTS];
482		String str = null; // a string element if present (must be last)
483		try {
484			int firstQuoteIndex = event.indexOf('"');
485			if ( firstQuoteIndex>=0 ) {
486				// treat specially; has a string argument like "a comment\n
487				// Note that the string is terminated by \n not end quote.
488				// Easier to parse that way.
489				String eventWithoutString = event.substring(0,firstQuoteIndex);
490				str = event.substring(firstQuoteIndex+1,event.length());
491				event = eventWithoutString;
492			}
493			StringTokenizer st = new StringTokenizer(event, "\t", false);
494			int i = 0;
495			while ( st.hasMoreTokens() ) {
496				if ( i>=MAX_EVENT_ELEMENTS ) {
497					// ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event);
498					return elements;
499				}
500				elements[i] = st.nextToken();
501				i++;
502			}
503			if ( str!=null ) {
504				elements[i] = str;
505			}
506		}
507		catch (Exception e) {
508			e.printStackTrace(System.err);
509		}
510		return elements;
511	}
512
513	protected String unEscapeNewlines(String txt) {
514		// this unescape is slow but easy to understand
515		txt = txt.replaceAll("%0A","\n");  // unescape \n
516		txt = txt.replaceAll("%0D","\r");  // unescape \r
517		txt = txt.replaceAll("%25","%");   // undo escaped escape chars
518		return txt;
519	}
520
521	public boolean tokenIndexesAreInvalid() {
522		return false;
523		//return tokenIndexesInvalid;
524	}
525
526}
527
528