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.BaseRecognizer;
33import org.antlr.runtime.tree.TreeAdaptor;
34
35import java.io.*;
36import java.net.ServerSocket;
37import java.net.Socket;
38
39/** A proxy debug event listener that forwards events over a socket to
40 *  a debugger (or any other listener) using a simple text-based protocol;
41 *  one event per line.  ANTLRWorks listens on server socket with a
42 *  RemoteDebugEventSocketListener instance.  These two objects must therefore
43 *  be kept in sync.  New events must be handled on both sides of socket.
44 */
45public class DebugEventSocketProxy extends BlankDebugEventListener {
46	public static final int DEFAULT_DEBUGGER_PORT = 49100; // was 49153
47	protected int port = DEFAULT_DEBUGGER_PORT;
48	protected ServerSocket serverSocket;
49	protected Socket socket;
50	protected String grammarFileName;
51	protected PrintWriter out;
52	protected BufferedReader in;
53
54	/** Who am i debugging? */
55	protected BaseRecognizer recognizer;
56
57	/** Almost certainly the recognizer will have adaptor set, but
58	 *  we don't know how to cast it (Parser or TreeParser) to get
59	 *  the adaptor field.  Must be set with a constructor. :(
60	 */
61	protected TreeAdaptor adaptor;
62
63	public DebugEventSocketProxy(BaseRecognizer recognizer, TreeAdaptor adaptor) {
64		this(recognizer, DEFAULT_DEBUGGER_PORT, adaptor);
65	}
66
67	public DebugEventSocketProxy(BaseRecognizer recognizer, int port, TreeAdaptor adaptor) {
68		this.grammarFileName = recognizer.getGrammarFileName();
69		this.adaptor = adaptor;
70		this.port = port;
71	}
72
73	public void handshake() throws IOException {
74		if ( serverSocket==null ) {
75			serverSocket = new ServerSocket(port);
76			socket = serverSocket.accept();
77			socket.setTcpNoDelay(true);
78			OutputStream os = socket.getOutputStream();
79			OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
80			out = new PrintWriter(new BufferedWriter(osw));
81			InputStream is = socket.getInputStream();
82			InputStreamReader isr = new InputStreamReader(is, "UTF8");
83			in = new BufferedReader(isr);
84			out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION);
85			out.println("grammar \""+ grammarFileName);
86			out.flush();
87			ack();
88		}
89	}
90
91	public void commence() {
92		// don't bother sending event; listener will trigger upon connection
93	}
94
95	public void terminate() {
96		transmit("terminate");
97		out.close();
98		try {
99			socket.close();
100		}
101		catch (IOException ioe) {
102			ioe.printStackTrace(System.err);
103		}
104	}
105
106	protected void ack() {
107		try {
108			in.readLine();
109		}
110		catch (IOException ioe) {
111			ioe.printStackTrace(System.err);
112		}
113	}
114
115	protected void transmit(String event) {
116		out.println(event);
117		out.flush();
118		ack();
119	}
120
121	public void enterRule(String grammarFileName, String ruleName) {
122		transmit("enterRule\t"+grammarFileName+"\t"+ruleName);
123	}
124
125	public void enterAlt(int alt) {
126		transmit("enterAlt\t"+alt);
127	}
128
129	public void exitRule(String grammarFileName, String ruleName) {
130		transmit("exitRule\t"+grammarFileName+"\t"+ruleName);
131	}
132
133	public void enterSubRule(int decisionNumber) {
134		transmit("enterSubRule\t"+decisionNumber);
135	}
136
137	public void exitSubRule(int decisionNumber) {
138		transmit("exitSubRule\t"+decisionNumber);
139	}
140
141	public void enterDecision(int decisionNumber, boolean couldBacktrack) {
142		transmit("enterDecision\t"+decisionNumber+"\t"+couldBacktrack);
143	}
144
145	public void exitDecision(int decisionNumber) {
146		transmit("exitDecision\t"+decisionNumber);
147	}
148
149	public void consumeToken(Token t) {
150		String buf = serializeToken(t);
151		transmit("consumeToken\t"+buf);
152	}
153
154	public void consumeHiddenToken(Token t) {
155		String buf = serializeToken(t);
156		transmit("consumeHiddenToken\t"+buf);
157	}
158
159	public void LT(int i, Token t) {
160        if(t != null)
161            transmit("LT\t"+i+"\t"+serializeToken(t));
162	}
163
164	public void mark(int i) {
165		transmit("mark\t"+i);
166	}
167
168	public void rewind(int i) {
169		transmit("rewind\t"+i);
170	}
171
172	public void rewind() {
173		transmit("rewind");
174	}
175
176	public void beginBacktrack(int level) {
177		transmit("beginBacktrack\t"+level);
178	}
179
180	public void endBacktrack(int level, boolean successful) {
181		transmit("endBacktrack\t"+level+"\t"+(successful?TRUE:FALSE));
182	}
183
184	public void location(int line, int pos) {
185		transmit("location\t"+line+"\t"+pos);
186	}
187
188	public void recognitionException(RecognitionException e) {
189		StringBuffer buf = new StringBuffer(50);
190		buf.append("exception\t");
191		buf.append(e.getClass().getName());
192		// dump only the data common to all exceptions for now
193		buf.append("\t");
194		buf.append(e.index);
195		buf.append("\t");
196		buf.append(e.line);
197		buf.append("\t");
198		buf.append(e.charPositionInLine);
199		transmit(buf.toString());
200	}
201
202	public void beginResync() {
203		transmit("beginResync");
204	}
205
206	public void endResync() {
207		transmit("endResync");
208	}
209
210	public void semanticPredicate(boolean result, String predicate) {
211		StringBuffer buf = new StringBuffer(50);
212		buf.append("semanticPredicate\t");
213		buf.append(result);
214		serializeText(buf, predicate);
215		transmit(buf.toString());
216	}
217
218	// A S T  P a r s i n g  E v e n t s
219
220	public void consumeNode(Object t) {
221		StringBuffer buf = new StringBuffer(50);
222		buf.append("consumeNode");
223		serializeNode(buf, t);
224		transmit(buf.toString());
225	}
226
227	public void LT(int i, Object t) {
228		int ID = adaptor.getUniqueID(t);
229		String text = adaptor.getText(t);
230		int type = adaptor.getType(t);
231		StringBuffer buf = new StringBuffer(50);
232		buf.append("LN\t"); // lookahead node; distinguish from LT in protocol
233		buf.append(i);
234		serializeNode(buf, t);
235		transmit(buf.toString());
236	}
237
238	protected void serializeNode(StringBuffer buf, Object t) {
239		int ID = adaptor.getUniqueID(t);
240		String text = adaptor.getText(t);
241		int type = adaptor.getType(t);
242		buf.append("\t");
243		buf.append(ID);
244		buf.append("\t");
245		buf.append(type);
246		Token token = adaptor.getToken(t);
247		int line = -1;
248		int pos = -1;
249		if ( token!=null ) {
250			line = token.getLine();
251			pos = token.getCharPositionInLine();
252		}
253		buf.append("\t");
254		buf.append(line);
255		buf.append("\t");
256		buf.append(pos);
257		int tokenIndex = adaptor.getTokenStartIndex(t);
258		buf.append("\t");
259		buf.append(tokenIndex);
260		serializeText(buf, text);
261	}
262
263
264	// A S T  E v e n t s
265
266	public void nilNode(Object t) {
267		int ID = adaptor.getUniqueID(t);
268		transmit("nilNode\t"+ID);
269	}
270
271	public void errorNode(Object t) {
272		int ID = adaptor.getUniqueID(t);
273		String text = t.toString();
274		StringBuffer buf = new StringBuffer(50);
275		buf.append("errorNode\t");
276		buf.append(ID);
277		buf.append("\t");
278		buf.append(Token.INVALID_TOKEN_TYPE);
279		serializeText(buf, text);
280		transmit(buf.toString());
281	}
282
283	public void createNode(Object t) {
284		int ID = adaptor.getUniqueID(t);
285		String text = adaptor.getText(t);
286		int type = adaptor.getType(t);
287		StringBuffer buf = new StringBuffer(50);
288		buf.append("createNodeFromTokenElements\t");
289		buf.append(ID);
290		buf.append("\t");
291		buf.append(type);
292		serializeText(buf, text);
293		transmit(buf.toString());
294	}
295
296	public void createNode(Object node, Token token) {
297		int ID = adaptor.getUniqueID(node);
298		int tokenIndex = token.getTokenIndex();
299		transmit("createNode\t"+ID+"\t"+tokenIndex);
300	}
301
302	public void becomeRoot(Object newRoot, Object oldRoot) {
303		int newRootID = adaptor.getUniqueID(newRoot);
304		int oldRootID = adaptor.getUniqueID(oldRoot);
305		transmit("becomeRoot\t"+newRootID+"\t"+oldRootID);
306	}
307
308	public void addChild(Object root, Object child) {
309		int rootID = adaptor.getUniqueID(root);
310		int childID = adaptor.getUniqueID(child);
311		transmit("addChild\t"+rootID+"\t"+childID);
312	}
313
314	public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {
315		int ID = adaptor.getUniqueID(t);
316		transmit("setTokenBoundaries\t"+ID+"\t"+tokenStartIndex+"\t"+tokenStopIndex);
317	}
318
319
320    // support
321
322    public void setTreeAdaptor(TreeAdaptor adaptor) { this.adaptor = adaptor; }
323    public TreeAdaptor getTreeAdaptor() { return adaptor; }
324
325    protected String serializeToken(Token t) {
326        StringBuffer buf = new StringBuffer(50);
327        buf.append(t.getTokenIndex()); buf.append('\t');
328        buf.append(t.getType()); buf.append('\t');
329        buf.append(t.getChannel()); buf.append('\t');
330        buf.append(t.getLine()); buf.append('\t');
331		buf.append(t.getCharPositionInLine());
332		serializeText(buf, t.getText());
333		return buf.toString();
334	}
335
336	protected void serializeText(StringBuffer buf, String text) {
337		buf.append("\t\"");
338		if ( text==null ) {
339			text = "";
340		}
341		// escape \n and \r all text for token appears to exist on one line
342		// this escape is slow but easy to understand
343		text = escapeNewlines(text);
344		buf.append(text);
345	}
346
347	protected String escapeNewlines(String txt) {
348		txt = txt.replaceAll("%","%25");   // escape all escape char ;)
349		txt = txt.replaceAll("\n","%0A");  // escape \n
350		txt = txt.replaceAll("\r","%0D");  // escape \r
351		return txt;
352	}
353}
354
355