KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > debugger > tivo > DBRecorder


1 /*
2
3 [The "BSD licence"]
4 Copyright (c) 2005 Jean Bovet
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products
17 derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 */

31
32 package org.antlr.works.debugger.tivo;
33
34 import org.antlr.xjlib.appkit.utils.XJAlert;
35 import org.antlr.xjlib.appkit.utils.XJDialogProgress;
36 import org.antlr.xjlib.appkit.utils.XJDialogProgressDelegate;
37 import org.antlr.xjlib.foundation.XJUtils;
38 import org.antlr.runtime.Token;
39 import org.antlr.runtime.debug.RemoteDebugEventSocketListener;
40 import org.antlr.works.debugger.Debugger;
41 import org.antlr.works.debugger.events.*;
42 import org.antlr.works.prefs.AWPrefs;
43 import org.antlr.works.utils.Console;
44 import org.antlr.works.utils.NumberSet;
45
46 import javax.swing.*;
47 import java.io.IOException JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.List JavaDoc;
50 import java.util.Set JavaDoc;
51
52 public class DBRecorder implements Runnable JavaDoc, XJDialogProgressDelegate {
53
54     public static final int STATUS_STOPPED = 0;
55     public static final int STATUS_STOPPING = 1;
56     public static final int STATUS_LAUNCHING = 2;
57     public static final int STATUS_RUNNING = 3;
58     public static final int STATUS_BREAK = 4;
59
60     public static final int MAX_RETRY = 12;
61
62     protected Debugger debugger;
63     protected int status = STATUS_STOPPED;
64     protected boolean cancelled;
65
66     protected String JavaDoc address;
67     protected int port;
68
69     protected ArrayList JavaDoc<DBEvent> events;
70     protected int position;
71     protected NumberSet breakEvents = new NumberSet();
72     protected int stoppedOnEvent = DBEvent.NO_EVENT;
73     protected boolean ignoreBreakpoints = false;
74     protected StepOver stepOver = new StepOver();
75
76     protected int lastTokenIndexEventNumber;
77     protected int currentTokenIndexEventNumber;
78     protected int currentTokenIndex;
79
80     protected DBRecorderEventListener eventListener;
81     protected RemoteDebugEventSocketListener listener;
82
83     protected XJDialogProgress progress;
84
85     /** This flag is used to indicate that the debugger received the terminate event.
86      * It is used to force stop the debugger if it cannot be stopped by the normal method.
87      */

88     protected boolean debuggerReceivedTerminateEvent;
89
90     /** Flag used to indicate if the user has been warning about a problem
91      * with the remote parser state. It ensure the message is only displayed once
92      * during a debugging session.
93      */

94     protected boolean remoteParserStateWarned = false;
95
96     public DBRecorder(Debugger debugger) {
97         this.debugger = debugger;
98         reset();
99     }
100
101     public void showProgress() {
102         if(progress == null)
103             progress = new XJDialogProgress(debugger.getWindowComponent());
104         progress.setInfo("Connecting...");
105         progress.setIndeterminate(true);
106         progress.setDelegate(this);
107         progress.display();
108     }
109
110     public void hideProgress() {
111         progress.close();
112     }
113
114     /** Return true if the debugger is running */
115     public synchronized boolean isRunning() {
116         return status == DBRecorder.STATUS_RUNNING;
117     }
118
119     /** Return true if the debugger is alive (i.e. not stopped, stopping, starting) */
120     public synchronized boolean isAlive() {
121         return status == DBRecorder.STATUS_RUNNING ||
122                status == DBRecorder.STATUS_BREAK;
123     }
124
125     public synchronized void reset() {
126         if(events == null)
127             events = new ArrayList JavaDoc<DBEvent>();
128         else
129             events.clear();
130         position = -1;
131         currentTokenIndex = -1;
132         remoteParserStateWarned = false;
133     }
134
135     public synchronized DBEvent getEvent() {
136         if(position<0 || position>=events.size())
137             return null;
138         else
139             return events.get(position);
140     }
141
142     public synchronized DBEvent getLastEvent() {
143         return events.get(events.size()-1);
144     }
145
146     public synchronized List JavaDoc getCurrentEvents() {
147         if(events.size() == 0)
148             return (List JavaDoc)events.clone();
149
150         int toIndex = position+1;
151         if(toIndex >= events.size())
152             toIndex = events.size();
153
154         // Note: clone the list first in order to return
155
// a sublist that can be modified concurrently of the
156
// events list.
157
// Note that toIndex is exclusive for subList();
158
return ((List JavaDoc)events.clone()).subList(0, toIndex);
159     }
160
161     public void setPositionToEnd() {
162         position = events.size()-1;
163     }
164
165     public void setBreakEvents(Set events) {
166         this.breakEvents.replaceAll(events);
167     }
168
169     public Set getBreakEvents() {
170         return breakEvents;
171     }
172
173     public void setStoppedOnEvent(int event) {
174         stoppedOnEvent = event;
175     }
176
177     public int getStoppedOnEvent() {
178         return stoppedOnEvent;
179     }
180
181     public void setIgnoreBreakpoints(boolean flag) {
182         this.ignoreBreakpoints = flag;
183     }
184
185     public boolean ignoreBreakpoints() {
186         return ignoreBreakpoints;
187     }
188
189     public void queryGrammarBreakpoints() {
190         // Get the current breakpoints in the grammar text
191
// because they can be set/unset during a debugging
192
// session of course ;-)
193
debugger.queryGrammarBreakpoints();
194     }
195
196     /** Return true if the debugger hitted a break event */
197     public boolean isOnBreakEvent() {
198         int breakEvent = getOnBreakEvent();
199         if(breakEvent != DBEvent.NO_EVENT) {
200             setStoppedOnEvent(breakEvent);
201             setStatus(STATUS_BREAK);
202             return true;
203         } else
204             return false;
205     }
206
207     /** Return the event type that causes the break */
208     public int getOnBreakEvent() {
209         DBEvent event = getEvent();
210         if(event == null)
211             return DBEvent.NO_EVENT;
212
213         /** If we are stepping over handle it here */
214         if(stepOver.isSteppingOver()) {
215             if(stepOver.shouldStop(event)) {
216                 stepOver.endStepOver();
217                 return event.getEventType();
218             } else
219                 return DBEvent.NO_EVENT;
220         }
221
222         if(event.getEventType() == DBEvent.COMMENCE)
223             return event.getEventType();
224
225         if(breakEvents.contains(DBEvent.ALL))
226             return event.getEventType();
227
228         // Stop on debugger breakpoints
229
if(event.getEventType() == DBEvent.LOCATION && !ignoreBreakpoints())
230             if(debugger.isBreakpointAtLine(((DBEventLocation)event).line-1))
231                 return event.getEventType();
232
233         // Stop on input text breakpoint
234
if(event.getEventType() == DBEvent.CONSUME_TOKEN && !ignoreBreakpoints())
235             if(debugger.isBreakpointAtToken(((DBEventConsumeToken)event).token))
236                 return event.getEventType();
237
238         if(event.getEventType() == DBEvent.CONSUME_TOKEN && breakEvents.contains(DBEvent.CONSUME_TOKEN)) {
239             // Breaks only on consume token from channel 0
240
return ((DBEventConsumeToken)event).token.getChannel() == Token.DEFAULT_CHANNEL?event.getEventType() :DBEvent.NO_EVENT;
241         } else
242             return breakEvents.contains(event.getEventType())?event.getEventType() :DBEvent.NO_EVENT;
243     }
244
245     public synchronized void setStatus(int status) {
246         if(this.status != status) {
247             this.status = status;
248             debugger.recorderStatusDidChange();
249         }
250     }
251
252     public synchronized int getStatus() {
253         return status;
254     }
255
256     public boolean isAtBeginning() {
257         return position == 0;
258     }
259
260     public boolean isAtEnd() {
261         DBEvent e = getEvent();
262         if(e == null)
263             return true;
264         else
265             return e.getEventType() == DBEvent.TERMINATE;
266     }
267
268     public void stepBackward(Set breakEvents) {
269         setIgnoreBreakpoints(false);
270         stepContinue(breakEvents);
271         stepMove(-1);
272         /* Play the events in any case. Otherwise the debugger might not get notified
273          correctly of a backward step. */

274         playEvents(true);
275     }
276
277     public void stepForward(Set breakEvents) {
278         setIgnoreBreakpoints(false);
279         stepContinue(breakEvents);
280         if(stepMove(1)) {
281             /* There is some events left, play them */
282             playEvents(false);
283         } else {
284             /* No more events. If the debugger received the terminate event,
285                play the events so far. Otherwise, notify the thread that it can
286                continue to receive more events from the remote parser. */

287             if(debuggerReceivedTerminateEvent)
288                 playEvents(false);
289             else
290                 threadNotify();
291         }
292     }
293
294     public void stepOver() {
295         stepOver.beginStepOver();
296         fastForward();
297     }
298
299     public void stepContinue(Set breakEvents) {
300         setBreakEvents(breakEvents);
301         queryGrammarBreakpoints();
302         setStatus(STATUS_RUNNING);
303     }
304
305     /** This method returns false if no more event is available */
306     public boolean stepMove(int direction) {
307         position += direction;
308         if(position<0) {
309             position = 0;
310             return false;
311         }
312         if(position >= events.size()) {
313             position = events.size()-1;
314             return false;
315         }
316
317         DBEvent event;
318         while((event = getEvent()) != null) {
319             if(isOnBreakEvent())
320                 break;
321
322             position += direction;
323         }
324         if(event == null)
325             position -= direction;
326
327         return event != null;
328     }
329
330     public void goToStart() {
331         position = 0;
332         setIgnoreBreakpoints(false);
333         playEvents(true);
334     }
335
336     public void goToEnd() {
337         setIgnoreBreakpoints(true);
338         stepContinue(new NumberSet(DBEvent.TERMINATE));
339         if(stepMove(1))
340             playEvents(false);
341         else
342             threadNotify();
343     }
344
345     public void fastForward() {
346         stepForward(new NumberSet(DBEvent.TERMINATE));
347     }
348
349     public void connect(String JavaDoc address, int port) {
350         this.address = address;
351         this.port = port;
352
353         new Thread JavaDoc(this).start();
354     }
355
356     public void run() {
357         eventListener = new DBRecorderEventListener(this);
358         cancelled = false;
359
360         boolean connected = false;
361         boolean showProgress = false;
362
363         long t = System.currentTimeMillis();
364         long timeout = AWPrefs.getDebugLaunchTimeout()*1000;
365
366         while((System.currentTimeMillis()-t) < timeout && !cancelled) {
367             listener = null;
368             try {
369                 listener = new RemoteDebugEventSocketListener(eventListener,
370                         DBRecorder.this.address, DBRecorder.this.port);
371             } catch (IOException JavaDoc e) {
372                 listener = null;
373             }
374
375             if(listener != null) {
376                 connected = true;
377                 break;
378             }
379
380             if((System.currentTimeMillis()-t) >= 2 && !showProgress) {
381                 showProgress();
382                 showProgress = true;
383             }
384
385             try {
386                 Thread.sleep(500);
387             } catch (InterruptedException JavaDoc e) {
388                 // We don't care if the sleep has been interrupted
389
}
390         }
391
392         if(showProgress)
393             hideProgress();
394
395         if(cancelled) {
396             setStatus(STATUS_STOPPED);
397             connectionCancelled();
398         } else if(!connected) {
399             setStatus(STATUS_STOPPED);
400             connectionFailed();
401         } else {
402             setStatus(STATUS_LAUNCHING);
403
404             debuggerReceivedTerminateEvent = false;
405
406             reset();
407             listener.start();
408
409             connectionSuccess();
410         }
411     }
412
413     public void connectionSuccess() {
414         SwingUtilities.invokeLater(new Runnable JavaDoc() {
415             public void run() {
416                 debugger.connectionSuccess();
417             }
418         });
419     }
420
421     public void connectionFailed() {
422         SwingUtilities.invokeLater(new Runnable JavaDoc() {
423             public void run() {
424                 debugger.connectionFailed();
425             }
426         });
427     }
428
429     public void connectionCancelled() {
430         SwingUtilities.invokeLater(new Runnable JavaDoc() {
431             public void run() {
432                 debugger.connectionCancelled();
433             }
434         });
435     }
436
437     public synchronized void requestStop() {
438         setStatus(STATUS_STOPPING);
439         threadNotify();
440
441         if(debuggerReceivedTerminateEvent)
442             stop();
443     }
444
445     public void stop() {
446         setStatus(STATUS_STOPPED);
447         debugger.recorderDidStop();
448     }
449
450     /** This method checks that the remote parser's states are in sync with
451      * the state of the debugger (i.e. name of the grammar). This method
452      * is called in the event dispatch thread.
453      */

454     public void checkRemoteParserHeaders() {
455         //Tool.VERSION
456
//System.out.println(listener.version);
457

458         String JavaDoc grammarFileName = debugger.getGrammar().getFileName();
459         String JavaDoc remoteParserGrammarFileName = XJUtils.getLastPathComponent(listener.grammarFileName);
460
461         if(!grammarFileName.equals(remoteParserGrammarFileName)) {
462             String JavaDoc message = "Warning: the grammar used by the remote parser is not the same ("+remoteParserGrammarFileName+").";
463             XJAlert.display(debugger.getWindowComponent(), "Grammar Mismatch", message);
464         }
465     }
466
467     /** Check any error coming from the remote parser. Return true if the debugger needs
468       to be paused
469     */

470     public boolean checkRemoteParserState() {
471         if(remoteParserStateWarned)
472             return false;
473
474         if(listener.tokenIndexesAreInvalid()) {
475             remoteParserStateWarned = true;
476
477             SwingUtilities.invokeLater(new Runnable JavaDoc() {
478                 public void run() {
479                     String JavaDoc message = "Invalid token indexes (current index is "+currentTokenIndex+" at event "+currentTokenIndexEventNumber+" while the same index was used at event "+lastTokenIndexEventNumber+"). Make sure that the remote parser implements the getTokenIndex() method of Token. The indexes must be unique for each consumed token.";
480                     XJAlert.display(debugger.getWindowComponent(), "Invalid Token Indexes", message);
481                 }
482             });
483
484             return true;
485         }
486         return false;
487     }
488
489     /** This method keeps track of the last consumed index in order to display
490      * useful information if an invalid index is detected
491      */

492     public void recordIndexes(DBEvent event) {
493         Token t = null;
494         if(event instanceof DBEventConsumeToken) {
495             DBEventConsumeToken e = (DBEventConsumeToken) event;
496             t = e.token;
497         }
498         if(event instanceof DBEventConsumeHiddenToken) {
499             DBEventConsumeHiddenToken e = (DBEventConsumeHiddenToken) event;
500             t = e.token;
501         }
502
503         if(t != null) {
504             lastTokenIndexEventNumber = currentTokenIndexEventNumber;
505             currentTokenIndexEventNumber = events.size()-1;
506             currentTokenIndex = t.getTokenIndex();
507         }
508     }
509
510     /** This method is called by DBRecorderEventListener for each event received from
511      * the remote parser. It is running on another thread than the event thread.
512      */

513     public synchronized void listenerEvent(DBEvent event) {
514         events.add(event);
515         recordIndexes(event);
516         setPositionToEnd();
517
518         switch(getStatus()) {
519             case STATUS_LAUNCHING:
520                 setStatus(STATUS_RUNNING);
521                 break;
522
523             case STATUS_STOPPING:
524                 /* Stop the debugger if the terminate event is reached or if the flag
525                 debuggerReceivedTerminateEvent is true
526                 */

527                 if(event.getEventType() == DBEvent.TERMINATE || debuggerReceivedTerminateEvent)
528                     stop();
529                 break;
530         }
531
532         if(isRunning()) {
533             switch(event.getEventType()) {
534                 case DBEvent.TERMINATE:
535                     setStoppedOnEvent(DBEvent.TERMINATE);
536                     breaksOnEvent(false);
537                     debuggerReceivedTerminateEvent = true;
538                     break;
539
540                 case DBEvent.COMMENCE:
541                     SwingUtilities.invokeLater(new Runnable JavaDoc() {
542                         public void run() {
543                             checkRemoteParserHeaders();
544                         }
545                     });
546                     setStoppedOnEvent(DBEvent.COMMENCE);
547                     breaksOnEvent(true);
548                     break;
549
550                 default:
551                     if(checkRemoteParserState() || isOnBreakEvent())
552                         breaksOnEvent(true);
553                     break;
554             }
555         }
556     }
557
558     public synchronized void threadNotify() {
559         notify();
560     }
561
562     public synchronized void threadWait() {
563         try {
564             wait();
565         } catch (InterruptedException JavaDoc e) {
566             debugger.getConsole().println("recorderThreadBreaksOnEvent: interrupted", Console.LEVEL_WARNING);
567         }
568     }
569
570     public synchronized void breaksOnEvent(boolean wait) {
571         setStatus(STATUS_BREAK);
572         playEvents(false);
573         if(wait)
574             threadWait();
575     }
576
577     protected synchronized void playEvents(boolean reset) {
578         /** Make sure this method is called on the event dispatch thread */
579         if(!SwingUtilities.isEventDispatchThread())
580             SwingUtilities.invokeLater(new PlayEventRunnable(reset));
581         else
582             debugger.playEvents(getCurrentEvents(), reset);
583     }
584
585     public void dialogDidCancel() {
586         cancelled = true;
587     }
588
589     public class StepOver {
590
591         public static final int MODE_DISABLED = 0;
592         public static final int MODE_WAIT_ENTER_RULE = 1;
593         public static final int MODE_WAIT_EXIT_RULE = 2;
594         public static final int MODE_WAIT_LOCATION = 3;
595
596         public int mode = MODE_DISABLED;
597
598         /** Count the number of nested stepped over rule name */
599         public int nested;
600         /** Name of the stepped over rule */
601         public String JavaDoc ruleName;
602
603         public void beginStepOver() {
604             mode = MODE_WAIT_ENTER_RULE;
605         }
606
607         public void endStepOver() {
608             mode = MODE_DISABLED;
609         }
610
611         public boolean isSteppingOver() {
612             return mode != MODE_DISABLED;
613         }
614
615         public boolean shouldStop(DBEvent event) {
616             switch(mode) {
617                 case MODE_WAIT_ENTER_RULE:
618                     if(event instanceof DBEventEnterRule) {
619                         DBEventEnterRule e = (DBEventEnterRule)event;
620                         ruleName = e.name;
621                         mode = MODE_WAIT_EXIT_RULE;
622                         nested = 0;
623                     }
624                     break;
625
626                 case MODE_WAIT_EXIT_RULE:
627                     if(event instanceof DBEventEnterRule) {
628                         DBEventEnterRule e = (DBEventEnterRule)event;
629                         if(e.name.equals(ruleName))
630                             nested++;
631                     } else if(event instanceof DBEventExitRule) {
632                         DBEventExitRule e = (DBEventExitRule)event;
633                         if(e.name.equals(ruleName)) {
634                             if(nested == 0) {
635                                 mode = MODE_WAIT_LOCATION;
636                             } else {
637                                 nested--;
638                             }
639                         }
640                     }
641                     break;
642
643                 case MODE_WAIT_LOCATION:
644                     if(event instanceof DBEventLocation)
645                         return true;
646                     break;
647             }
648             return false;
649         }
650     }
651
652     public class PlayEventRunnable implements Runnable JavaDoc {
653
654         public boolean reset;
655
656         public PlayEventRunnable(boolean reset) {
657             this.reset = reset;
658         }
659
660         public void run() {
661             playEvents(reset);
662         }
663     }
664 }
665
Popular Tags