1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2005-2008 Terence Parr
4 * All rights reserved.
5 *
6 * Conversion to C#:
7 * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33namespace Antlr.Runtime.Debug
34{
35    using Antlr.Runtime.JavaExtensions;
36
37    using Array = System.Array;
38    using BaseTree = Antlr.Runtime.Tree.BaseTree;
39    using Console = System.Console;
40    using Exception = System.Exception;
41    using IOException = System.IO.IOException;
42    using ITree = Antlr.Runtime.Tree.ITree;
43    using Math = System.Math;
44    using Socket = System.Net.Sockets.Socket;
45    using SocketException = System.Net.Sockets.SocketException;
46    using TextReader = System.IO.TextReader;
47    using TextWriter = System.IO.TextWriter;
48
49    public class RemoteDebugEventSocketListener
50    {
51        const int MAX_EVENT_ELEMENTS = 8;
52        IDebugEventListener listener;
53        string machine;
54        int port;
55        Socket channel = null;
56        TextWriter @out;
57        TextReader @in;
58        string @event;
59        /** <summary>Version of ANTLR (dictates events)</summary> */
60        public string version;
61        public string grammarFileName;
62        /** <summary>
63         *  Track the last token index we saw during a consume.  If same, then
64         *  set a flag that we have a problem.
65         *  </summary>
66         */
67        int previousTokenIndex = -1;
68        bool tokenIndexesInvalid = false;
69
70        public class ProxyToken : IToken
71        {
72            int index;
73            int type;
74            int channel;
75            int line;
76            int charPos;
77            string text;
78            public ProxyToken( int index )
79            {
80                this.index = index;
81            }
82            public ProxyToken( int index, int type, int channel,
83                              int line, int charPos, string text )
84            {
85                this.index = index;
86                this.type = type;
87                this.channel = channel;
88                this.line = line;
89                this.charPos = charPos;
90                this.text = text;
91            }
92
93            #region IToken Members
94            public string Text
95            {
96                get
97                {
98                    return text;
99                }
100                set
101                {
102                    text = value;
103                }
104            }
105
106            public int Type
107            {
108                get
109                {
110                    return type;
111                }
112                set
113                {
114                    type = value;
115                }
116            }
117
118            public int Line
119            {
120                get
121                {
122                    return line;
123                }
124                set
125                {
126                    line = value;
127                }
128            }
129
130            public int CharPositionInLine
131            {
132                get
133                {
134                    return charPos;
135                }
136                set
137                {
138                    charPos = value;
139                }
140            }
141
142            public int Channel
143            {
144                get
145                {
146                    return channel;
147                }
148                set
149                {
150                    channel = value;
151                }
152            }
153
154            public int StartIndex
155            {
156                get
157                {
158                    return -1;
159                }
160                set
161                {
162                }
163            }
164
165            public int StopIndex
166            {
167                get
168                {
169                    return -1;
170                }
171                set
172                {
173                }
174            }
175
176            public int TokenIndex
177            {
178                get
179                {
180                    return index;
181                }
182                set
183                {
184                    index = value;
185                }
186            }
187
188            public ICharStream InputStream
189            {
190                get
191                {
192                    return null;
193                }
194                set
195                {
196                }
197            }
198
199            #endregion
200
201            public override string ToString()
202            {
203                string channelStr = "";
204                if ( channel != TokenChannels.Default )
205                {
206                    channelStr = ",channel=" + channel;
207                }
208                return "[" + Text + "/<" + type + ">" + channelStr + "," + line + ":" + CharPositionInLine + ",@" + index + "]";
209            }
210        }
211
212        public class ProxyTree : BaseTree
213        {
214            public int ID;
215            int type;
216            int line = 0;
217            public int charPos = -1;
218            public int tokenIndex = -1;
219            string text;
220
221            public ProxyTree( int ID, int type, int line, int charPos, int tokenIndex, string text )
222            {
223                this.ID = ID;
224                this.type = type;
225                this.line = line;
226                this.charPos = charPos;
227                this.tokenIndex = tokenIndex;
228                this.text = text;
229            }
230
231            public ProxyTree( int ID )
232            {
233                this.ID = ID;
234            }
235
236            #region Properties
237            public override string Text
238            {
239                get
240                {
241                    return text;
242                }
243                set
244                {
245                }
246            }
247            public override int TokenStartIndex
248            {
249                get
250                {
251                    return tokenIndex;
252                }
253                set
254                {
255                }
256            }
257            public override int TokenStopIndex
258            {
259                get
260                {
261                    return 0;
262                }
263                set
264                {
265                }
266            }
267            public override int Type
268            {
269                get
270                {
271                    return type;
272                }
273                set
274                {
275                }
276            }
277            #endregion
278
279            public override ITree DupNode()
280            {
281                return null;
282            }
283
284            public override string ToString()
285            {
286                return "fix this";
287            }
288        }
289
290        public RemoteDebugEventSocketListener( IDebugEventListener listener,
291                                              string machine,
292                                              int port )
293        {
294            this.listener = listener;
295            this.machine = machine;
296            this.port = port;
297
298            if ( !OpenConnection() )
299            {
300                throw new SocketException();
301            }
302        }
303
304        protected virtual void EventHandler()
305        {
306            try
307            {
308                Handshake();
309                @event = @in.ReadLine();
310                while ( @event != null )
311                {
312                    Dispatch( @event );
313                    Ack();
314                    @event = @in.ReadLine();
315                }
316            }
317            catch ( Exception e )
318            {
319                Console.Error.WriteLine( e );
320                ExceptionExtensions.PrintStackTrace( e, Console.Error );
321            }
322            finally
323            {
324                CloseConnection();
325            }
326        }
327
328        protected virtual bool OpenConnection()
329        {
330            bool success = false;
331            try
332            {
333                throw new System.NotImplementedException();
334                //channel = new Socket( machine, port );
335                //channel.setTcpNoDelay( true );
336                //OutputStream os = channel.getOutputStream();
337                //OutputStreamWriter osw = new OutputStreamWriter( os, "UTF8" );
338                //@out = new PrintWriter( new BufferedWriter( osw ) );
339                //InputStream @is = channel.getInputStream();
340                //InputStreamReader isr = new InputStreamReader( @is, "UTF8" );
341                //@in = new BufferedReader( isr );
342                //success = true;
343            }
344            catch ( Exception e )
345            {
346                Console.Error.WriteLine( e );
347            }
348            return success;
349        }
350
351        protected virtual void CloseConnection()
352        {
353            try
354            {
355                @in.Close();
356                @in = null;
357                @out.Close();
358                @out = null;
359                channel.Close();
360                channel = null;
361            }
362            catch ( Exception e )
363            {
364                Console.Error.WriteLine( e );
365                ExceptionExtensions.PrintStackTrace( e, Console.Error );
366            }
367            finally
368            {
369                if ( @in != null )
370                {
371                    try
372                    {
373                        @in.Close();
374                    }
375                    catch ( IOException ioe )
376                    {
377                        Console.Error.WriteLine( ioe );
378                    }
379                }
380                if ( @out != null )
381                {
382                    @out.Close();
383                }
384                if ( channel != null )
385                {
386                    try
387                    {
388                        channel.Close();
389                    }
390                    catch ( IOException ioe )
391                    {
392                        Console.Error.WriteLine( ioe );
393                    }
394                }
395            }
396
397        }
398
399        protected virtual void Handshake()
400        {
401            string antlrLine = @in.ReadLine();
402            string[] antlrElements = GetEventElements( antlrLine );
403            version = antlrElements[1];
404            string grammarLine = @in.ReadLine();
405            string[] grammarElements = GetEventElements( grammarLine );
406            grammarFileName = grammarElements[1];
407            Ack();
408            listener.Commence(); // inform listener after handshake
409        }
410
411        protected virtual void Ack()
412        {
413            @out.WriteLine( "ack" );
414            @out.Flush();
415        }
416
417        protected virtual void Dispatch( string line )
418        {
419            //JSystem.@out.println( "event: " + line );
420            string[] elements = GetEventElements( line );
421            if ( elements == null || elements[0] == null )
422            {
423                Console.Error.WriteLine( "unknown debug event: " + line );
424                return;
425            }
426            if ( elements[0].Equals( "enterRule" ) )
427            {
428                listener.EnterRule( elements[1], elements[2] );
429            }
430            else if ( elements[0].Equals( "exitRule" ) )
431            {
432                listener.ExitRule( elements[1], elements[2] );
433            }
434            else if ( elements[0].Equals( "enterAlt" ) )
435            {
436                listener.EnterAlt( int.Parse( elements[1] ) );
437            }
438            else if ( elements[0].Equals( "enterSubRule" ) )
439            {
440                listener.EnterSubRule( int.Parse( elements[1] ) );
441            }
442            else if ( elements[0].Equals( "exitSubRule" ) )
443            {
444                listener.ExitSubRule( int.Parse( elements[1] ) );
445            }
446            else if ( elements[0].Equals( "enterDecision" ) )
447            {
448                listener.EnterDecision(int.Parse(elements[1]), elements[2].Equals("true"));
449            }
450            else if ( elements[0].Equals( "exitDecision" ) )
451            {
452                listener.ExitDecision( int.Parse( elements[1] ) );
453            }
454            else if ( elements[0].Equals( "location" ) )
455            {
456                listener.Location( int.Parse( elements[1] ),
457                                  int.Parse( elements[2] ) );
458            }
459            else if ( elements[0].Equals( "consumeToken" ) )
460            {
461                ProxyToken t = DeserializeToken( elements, 1 );
462                if ( t.TokenIndex == previousTokenIndex )
463                {
464                    tokenIndexesInvalid = true;
465                }
466                previousTokenIndex = t.TokenIndex;
467                listener.ConsumeToken( t );
468            }
469            else if ( elements[0].Equals( "consumeHiddenToken" ) )
470            {
471                ProxyToken t = DeserializeToken( elements, 1 );
472                if ( t.TokenIndex == previousTokenIndex )
473                {
474                    tokenIndexesInvalid = true;
475                }
476                previousTokenIndex = t.TokenIndex;
477                listener.ConsumeHiddenToken( t );
478            }
479            else if ( elements[0].Equals( "LT" ) )
480            {
481                IToken t = DeserializeToken( elements, 2 );
482                listener.LT( int.Parse( elements[1] ), t );
483            }
484            else if ( elements[0].Equals( "mark" ) )
485            {
486                listener.Mark( int.Parse( elements[1] ) );
487            }
488            else if ( elements[0].Equals( "rewind" ) )
489            {
490                if ( elements[1] != null )
491                {
492                    listener.Rewind( int.Parse( elements[1] ) );
493                }
494                else
495                {
496                    listener.Rewind();
497                }
498            }
499            else if ( elements[0].Equals( "beginBacktrack" ) )
500            {
501                listener.BeginBacktrack( int.Parse( elements[1] ) );
502            }
503            else if ( elements[0].Equals( "endBacktrack" ) )
504            {
505                int level = int.Parse( elements[1] );
506                int successI = int.Parse( elements[2] );
507                listener.EndBacktrack( level, successI == DebugEventListenerConstants.True );
508            }
509            else if ( elements[0].Equals( "exception" ) )
510            {
511#if true
512                throw new System.NotImplementedException();
513#else
514                string excName = elements[1];
515                string indexS = elements[2];
516                string lineS = elements[3];
517                string posS = elements[4];
518                Class excClass = null;
519                try
520                {
521                    excClass = Class.forName( excName );
522                    RecognitionException e =
523                        (RecognitionException)excClass.newInstance();
524                    e.index = int.Parse( indexS );
525                    e.line = int.Parse( lineS );
526                    e.charPositionInLine = int.Parse( posS );
527                    listener.recognitionException( e );
528                }
529                catch ( ClassNotFoundException cnfe )
530                {
531                    Console.Error.println( "can't find class " + cnfe );
532                    cnfe.printStackTrace( Console.Error );
533                }
534                catch ( InstantiationException ie )
535                {
536                    Console.Error.println( "can't instantiate class " + ie );
537                    ie.printStackTrace( Console.Error );
538                }
539                catch ( IllegalAccessException iae )
540                {
541                    Console.Error.println( "can't access class " + iae );
542                    iae.printStackTrace( Console.Error );
543                }
544#endif
545            }
546            else if ( elements[0].Equals( "beginResync" ) )
547            {
548                listener.BeginResync();
549            }
550            else if ( elements[0].Equals( "endResync" ) )
551            {
552                listener.EndResync();
553            }
554            else if ( elements[0].Equals( "terminate" ) )
555            {
556                listener.Terminate();
557            }
558            else if ( elements[0].Equals( "semanticPredicate" ) )
559            {
560                bool result = bool.Parse( elements[1] );
561                string predicateText = elements[2];
562                predicateText = UnEscapeNewlines( predicateText );
563                listener.SemanticPredicate( result,
564                                           predicateText );
565            }
566            else if ( elements[0].Equals( "consumeNode" ) )
567            {
568                ProxyTree node = DeserializeNode( elements, 1 );
569                listener.ConsumeNode( node );
570            }
571            else if ( elements[0].Equals( "LN" ) )
572            {
573                int i = int.Parse( elements[1] );
574                ProxyTree node = DeserializeNode( elements, 2 );
575                listener.LT( i, node );
576            }
577            else if ( elements[0].Equals( "createNodeFromTokenElements" ) )
578            {
579                int ID = int.Parse( elements[1] );
580                int type = int.Parse( elements[2] );
581                string text = elements[3];
582                text = UnEscapeNewlines( text );
583                ProxyTree node = new ProxyTree( ID, type, -1, -1, -1, text );
584                listener.CreateNode( node );
585            }
586            else if ( elements[0].Equals( "createNode" ) )
587            {
588                int ID = int.Parse( elements[1] );
589                int tokenIndex = int.Parse( elements[2] );
590                // create dummy node/token filled with ID, tokenIndex
591                ProxyTree node = new ProxyTree( ID );
592                ProxyToken token = new ProxyToken( tokenIndex );
593                listener.CreateNode( node, token );
594            }
595            else if ( elements[0].Equals( "nilNode" ) )
596            {
597                int ID = int.Parse( elements[1] );
598                ProxyTree node = new ProxyTree( ID );
599                listener.NilNode( node );
600            }
601            else if ( elements[0].Equals( "errorNode" ) )
602            {
603                // TODO: do we need a special tree here?
604                int ID = int.Parse( elements[1] );
605                int type = int.Parse( elements[2] );
606                string text = elements[3];
607                text = UnEscapeNewlines( text );
608                ProxyTree node = new ProxyTree( ID, type, -1, -1, -1, text );
609                listener.ErrorNode( node );
610            }
611            else if ( elements[0].Equals( "becomeRoot" ) )
612            {
613                int newRootID = int.Parse( elements[1] );
614                int oldRootID = int.Parse( elements[2] );
615                ProxyTree newRoot = new ProxyTree( newRootID );
616                ProxyTree oldRoot = new ProxyTree( oldRootID );
617                listener.BecomeRoot( newRoot, oldRoot );
618            }
619            else if ( elements[0].Equals( "addChild" ) )
620            {
621                int rootID = int.Parse( elements[1] );
622                int childID = int.Parse( elements[2] );
623                ProxyTree root = new ProxyTree( rootID );
624                ProxyTree child = new ProxyTree( childID );
625                listener.AddChild( root, child );
626            }
627            else if ( elements[0].Equals( "setTokenBoundaries" ) )
628            {
629                int ID = int.Parse( elements[1] );
630                ProxyTree node = new ProxyTree( ID );
631                listener.SetTokenBoundaries(
632                    node,
633                    int.Parse( elements[2] ),
634                    int.Parse( elements[3] ) );
635            }
636            else
637            {
638                Console.Error.WriteLine( "unknown debug event: " + line );
639            }
640        }
641
642        protected virtual ProxyTree DeserializeNode( string[] elements, int offset )
643        {
644            int ID = int.Parse( elements[offset + 0] );
645            int type = int.Parse( elements[offset + 1] );
646            int tokenLine = int.Parse( elements[offset + 2] );
647            int charPositionInLine = int.Parse( elements[offset + 3] );
648            int tokenIndex = int.Parse( elements[offset + 4] );
649            string text = elements[offset + 5];
650            text = UnEscapeNewlines( text );
651            return new ProxyTree( ID, type, tokenLine, charPositionInLine, tokenIndex, text );
652        }
653
654        protected virtual ProxyToken DeserializeToken( string[] elements,
655                                              int offset )
656        {
657            string indexS = elements[offset + 0];
658            string typeS = elements[offset + 1];
659            string channelS = elements[offset + 2];
660            string lineS = elements[offset + 3];
661            string posS = elements[offset + 4];
662            string text = elements[offset + 5];
663            text = UnEscapeNewlines( text );
664            int index = int.Parse( indexS );
665            ProxyToken t =
666                new ProxyToken( index,
667                               int.Parse( typeS ),
668                               int.Parse( channelS ),
669                               int.Parse( lineS ),
670                               int.Parse( posS ),
671                               text );
672            return t;
673        }
674
675        /** <summary>Create a thread to listen to the remote running recognizer</summary> */
676        public virtual void Start()
677        {
678            System.Threading.Thread t = new System.Threading.Thread( Run );
679            t.Start();
680        }
681
682        public virtual void Run()
683        {
684            EventHandler();
685        }
686
687        #region Misc
688
689        public virtual string[] GetEventElements( string @event )
690        {
691            if ( @event == null )
692            {
693                return null;
694            }
695            string[] elements = new string[MAX_EVENT_ELEMENTS];
696            string str = null; // a string element if present (must be last)
697            try
698            {
699                int firstQuoteIndex = @event.IndexOf( '"' );
700                if ( firstQuoteIndex >= 0 )
701                {
702                    // treat specially; has a string argument like "a comment\n
703                    // Note that the string is terminated by \n not end quote.
704                    // Easier to parse that way.
705                    string eventWithoutString = @event.Substring( 0, firstQuoteIndex );
706                    str = @event.Substring( firstQuoteIndex + 1 );
707                    @event = eventWithoutString;
708                }
709
710                string[] tokens = @event.Split('\t');
711                Array.Copy(tokens, elements, Math.Min(tokens.Length, MAX_EVENT_ELEMENTS));
712                if (tokens.Length >= MAX_EVENT_ELEMENTS)
713                    return elements;
714
715                if ( str != null )
716                {
717                    elements[tokens.Length] = str;
718                }
719            }
720            catch ( Exception e )
721            {
722                ExceptionExtensions.PrintStackTrace( e, Console.Error );
723            }
724            return elements;
725        }
726
727        protected virtual string UnEscapeNewlines( string txt )
728        {
729            // this unescape is slow but easy to understand
730            txt = txt.Replace( "%0A", "\n" );  // unescape \n
731            txt = txt.Replace( "%0D", "\r" );  // unescape \r
732            txt = txt.Replace( "%25", "%" );   // undo escaped escape chars
733            return txt;
734        }
735
736        public virtual bool TokenIndexesAreInvalid()
737        {
738            return tokenIndexesInvalid;
739        }
740
741        #endregion
742
743    }
744}
745