1 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 ; 48 import java.util.ArrayList ; 49 import java.util.List ; 50 import java.util.Set ; 51 52 public class DBRecorder implements Runnable , 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 address; 67 protected int port; 68 69 protected ArrayList <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 88 protected boolean debuggerReceivedTerminateEvent; 89 90 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 115 public synchronized boolean isRunning() { 116 return status == DBRecorder.STATUS_RUNNING; 117 } 118 119 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 <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 getCurrentEvents() { 147 if(events.size() == 0) 148 return (List )events.clone(); 149 150 int toIndex = position+1; 151 if(toIndex >= events.size()) 152 toIndex = events.size(); 153 154 return ((List )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 debugger.queryGrammarBreakpoints(); 194 } 195 196 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 208 public int getOnBreakEvent() { 209 DBEvent event = getEvent(); 210 if(event == null) 211 return DBEvent.NO_EVENT; 212 213 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 if(event.getEventType() == DBEvent.LOCATION && !ignoreBreakpoints()) 230 if(debugger.isBreakpointAtLine(((DBEventLocation)event).line-1)) 231 return event.getEventType(); 232 233 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 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 274 playEvents(true); 275 } 276 277 public void stepForward(Set breakEvents) { 278 setIgnoreBreakpoints(false); 279 stepContinue(breakEvents); 280 if(stepMove(1)) { 281 282 playEvents(false); 283 } else { 284 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 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 address, int port) { 350 this.address = address; 351 this.port = port; 352 353 new Thread (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 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 e) { 388 } 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 () { 415 public void run() { 416 debugger.connectionSuccess(); 417 } 418 }); 419 } 420 421 public void connectionFailed() { 422 SwingUtilities.invokeLater(new Runnable () { 423 public void run() { 424 debugger.connectionFailed(); 425 } 426 }); 427 } 428 429 public void connectionCancelled() { 430 SwingUtilities.invokeLater(new Runnable () { 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 454 public void checkRemoteParserHeaders() { 455 458 String grammarFileName = debugger.getGrammar().getFileName(); 459 String remoteParserGrammarFileName = XJUtils.getLastPathComponent(listener.grammarFileName); 460 461 if(!grammarFileName.equals(remoteParserGrammarFileName)) { 462 String 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 470 public boolean checkRemoteParserState() { 471 if(remoteParserStateWarned) 472 return false; 473 474 if(listener.tokenIndexesAreInvalid()) { 475 remoteParserStateWarned = true; 476 477 SwingUtilities.invokeLater(new Runnable () { 478 public void run() { 479 String 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 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 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 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 () { 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 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 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 599 public int nested; 600 601 public String 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 { 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 |