1 31 32 package org.antlr.works.debugger; 33 34 import org.antlr.runtime.ClassicToken; 35 import org.antlr.runtime.Token; 36 import org.antlr.works.ate.syntax.misc.ATELine; 37 import org.antlr.works.components.grammar.CEditorGrammar; 38 import org.antlr.works.debugger.events.DBEvent; 39 import org.antlr.works.debugger.input.DBInputTextTokenInfo; 40 import org.antlr.works.debugger.local.DBLocal; 41 import org.antlr.works.debugger.panels.*; 42 import org.antlr.works.debugger.remote.DBRemoteConnectDialog; 43 import org.antlr.works.debugger.tivo.DBPlayer; 44 import org.antlr.works.debugger.tivo.DBPlayerContextInfo; 45 import org.antlr.works.debugger.tivo.DBRecorder; 46 import org.antlr.works.debugger.tree.DBASTModel; 47 import org.antlr.works.debugger.tree.DBASTPanel; 48 import org.antlr.works.debugger.tree.DBParseTreeModel; 49 import org.antlr.works.debugger.tree.DBParseTreePanel; 50 import org.antlr.works.editor.EditorConsole; 51 import org.antlr.works.editor.EditorMenu; 52 import org.antlr.works.editor.EditorProvider; 53 import org.antlr.works.editor.EditorTab; 54 import org.antlr.works.grammar.EngineGrammar; 55 import org.antlr.works.menu.ContextualMenuFactory; 56 import org.antlr.works.prefs.AWPrefs; 57 import org.antlr.works.stats.StatisticsAW; 58 import org.antlr.works.swing.CustomSplitPanel; 59 import org.antlr.works.swing.CustomToggleButton; 60 import org.antlr.works.swing.DetachablePanel; 61 import org.antlr.works.swing.DetachablePanelDelegate; 62 import org.antlr.works.syntax.element.ElementBlock; 63 import org.antlr.works.syntax.element.ElementGrammarName; 64 import org.antlr.works.syntax.element.ElementRule; 65 import org.antlr.works.utils.Console; 66 import org.antlr.works.utils.Utils; 67 import org.antlr.xjlib.appkit.frame.XJDialog; 68 import org.antlr.xjlib.appkit.gview.GView; 69 import org.antlr.xjlib.appkit.utils.XJAlert; 70 import org.antlr.xjlib.foundation.notification.XJNotificationCenter; 71 72 import javax.swing.*; 73 import java.awt.*; 74 import java.awt.event.ActionEvent ; 75 import java.awt.event.ActionListener ; 76 import java.util.HashMap ; 77 import java.util.List ; 78 import java.util.Map ; 79 import java.util.Set ; 80 81 public class Debugger extends EditorTab implements DetachablePanelDelegate { 82 83 public static final String DEFAULT_LOCAL_ADDRESS = "localhost"; 84 85 public static final String NOTIF_DEBUG_STARTED = "NOTIF_DEBUG_STARTED"; 86 public static final String NOTIF_DEBUG_STOPPED = "NOTIF_DEBUG_STOPPED"; 87 88 public static final int OPTION_NONE = 0; 89 public static final int OPTION_AGAIN = 1; 90 public static final int OPTION_BUILD = 2; 91 92 public static final float PERCENT_WIDTH_LEFT = 0.2f; 93 public static final float PERCENT_WIDTH_MIDDLE = 0.5f; 94 95 protected JPanel panel; 96 97 protected DBInputPanel inputPanel; 98 protected DBOutputPanel outputPanel; 99 100 protected DBParseTreePanel parseTreePanel; 101 protected DBParseTreeModel parseTreeModel; 102 103 protected DBASTPanel astPanel; 104 protected DBASTModel astModel; 105 106 protected DBStackPanel stackPanel; 107 protected DBEventsPanel eventsPanel; 108 109 protected DBControlPanel controlPanel; 110 111 protected CustomSplitPanel splitPanel; 112 protected Map <Component,CustomToggleButton> components2toggle; 113 114 protected CEditorGrammar editor; 115 116 protected Set <Integer > breakpoints; 117 118 protected DBLocal local; 119 protected DBRecorder recorder; 120 protected DBPlayer player; 121 122 protected boolean running; 123 protected long dateOfModificationOnDisk = 0; 124 125 protected int debuggerCursorIndex = -1; 126 127 public Debugger(CEditorGrammar editor) { 128 this.editor = editor; 129 } 130 131 public void awake() { 132 panel = new JPanel(new BorderLayout()); 133 splitPanel = new CustomSplitPanel(); 134 components2toggle = new HashMap <Component, CustomToggleButton>(); 135 136 controlPanel = new DBControlPanel(this); 137 138 inputPanel = new DBInputPanel(this); 139 inputPanel.setTag(CustomSplitPanel.LEFT_INDEX); 140 outputPanel = new DBOutputPanel(this); 141 outputPanel.setTag(CustomSplitPanel.LEFT_INDEX); 142 143 parseTreePanel = new DBParseTreePanel(this); 144 parseTreePanel.setTag(CustomSplitPanel.MIDDLE_INDEX); 145 parseTreeModel = parseTreePanel.getModel(); 146 147 astPanel = new DBASTPanel(this); 148 astPanel.setTag(CustomSplitPanel.MIDDLE_INDEX); 149 astModel = astPanel.getModel(); 150 151 stackPanel = new DBStackPanel(this); 152 stackPanel.setTag(CustomSplitPanel.RIGHT_INDEX); 153 eventsPanel = new DBEventsPanel(this); 154 eventsPanel.setTag(CustomSplitPanel.RIGHT_INDEX); 155 156 panel.add(controlPanel, BorderLayout.NORTH); 157 panel.add(splitPanel, BorderLayout.CENTER); 158 panel.add(createToggleButtons(), BorderLayout.SOUTH); 159 160 local = new DBLocal(this); 161 recorder = new DBRecorder(this); 162 player = new DBPlayer(this); 163 164 updateStatusInfo(); 165 } 166 167 public void componentShouldLayout(Dimension size) { 168 assemblePanelsIntoSplitPane(size.width); 169 astPanel.componentShouldLayout(new Dimension((int) (size.width*PERCENT_WIDTH_MIDDLE), size.height)); 170 } 171 172 public static final int TOGGLE_INPUT = 0; 173 public static final int TOGGLE_OUTPUT = 1; 174 public static final int TOGGLE_PTREE = 2; 175 public static final int TOGGLE_AST = 3; 176 public static final int TOGGLE_STACK = 4; 177 public static final int TOGGLE_EVENTS = 5; 178 179 public Box createToggleButtons() { 180 Box b = Box.createHorizontalBox(); 181 b.add(createToggleButton("Input", TOGGLE_INPUT, inputPanel)); 182 b.add(createToggleButton("Output", TOGGLE_OUTPUT, outputPanel)); 183 b.add(Box.createHorizontalStrut(15)); 184 b.add(createToggleButton("Parse Tree", TOGGLE_PTREE, parseTreePanel)); 185 b.add(createToggleButton("AST", TOGGLE_AST, astPanel)); 186 b.add(Box.createHorizontalStrut(15)); 187 b.add(createToggleButton("Stack", TOGGLE_STACK, stackPanel)); 188 b.add(createToggleButton("Events", TOGGLE_EVENTS, eventsPanel)); 189 b.add(Box.createHorizontalGlue()); 190 return b; 191 } 192 193 public JToggleButton createToggleButton(String title, int tag, Component c) { 194 CustomToggleButton b = new CustomToggleButton(title); 195 b.setTag(tag); 196 b.setFocusable(false); 197 b.addActionListener(new ActionListener () { 198 public void actionPerformed(ActionEvent e) { 199 performToggleButtonAction((CustomToggleButton)e.getSource()); 200 } 201 202 }); 203 components2toggle.put(c, b); 204 return b; 205 } 206 207 public void assemblePanelsIntoSplitPane(int width) { 208 setComponentVisible(inputPanel, true); 209 setComponentVisible(outputPanel, false); 210 211 setComponentVisible(parseTreePanel, true); 212 setComponentVisible(astPanel, false); 213 214 setComponentVisible(stackPanel, true); 215 setComponentVisible(eventsPanel, false); 216 217 splitPanel.setComponentWidth(inputPanel, width*PERCENT_WIDTH_LEFT); 218 splitPanel.setComponentWidth(outputPanel, width*PERCENT_WIDTH_LEFT); 219 220 splitPanel.setComponentWidth(parseTreePanel, width*PERCENT_WIDTH_MIDDLE); 221 splitPanel.setComponentWidth(astPanel, width*PERCENT_WIDTH_MIDDLE); 222 223 splitPanel.setComponents(inputPanel, parseTreePanel, stackPanel); 224 225 230 } 231 232 public void setComponentVisible(Component c, boolean flag) { 233 c.setVisible(flag); 234 JToggleButton b = components2toggle.get(c); 235 b.setSelected(flag); 236 } 237 238 public void performToggleButtonAction(CustomToggleButton button) { 239 switch(button.getTag()) { 240 case TOGGLE_INPUT: 241 toggleComponents(inputPanel, outputPanel, CustomSplitPanel.LEFT_INDEX); 242 break; 243 case TOGGLE_OUTPUT: 244 toggleComponents(outputPanel, inputPanel, CustomSplitPanel.LEFT_INDEX); 245 break; 246 247 case TOGGLE_PTREE: 248 toggleComponents(parseTreePanel, astPanel, CustomSplitPanel.MIDDLE_INDEX); 249 break; 250 case TOGGLE_AST: 251 toggleComponents(astPanel, parseTreePanel, CustomSplitPanel.MIDDLE_INDEX); 252 break; 253 254 case TOGGLE_STACK: 255 toggleComponents(stackPanel, eventsPanel, CustomSplitPanel.RIGHT_INDEX); 256 break; 257 case TOGGLE_EVENTS: 258 toggleComponents(eventsPanel, stackPanel, CustomSplitPanel.RIGHT_INDEX); 259 break; 260 } 261 } 262 263 public void toggleComponents(DetachablePanel c, DetachablePanel other, int index) { 264 c.setVisible(!c.isVisible()); 265 if(c.isVisible()) { 266 if(!other.isDetached()) 267 setComponentVisible(other, false); 268 if(!c.isDetached()) 269 splitPanel.setComponent(c, index); 270 } else { 271 if(other.isVisible() && !other.isDetached()) 272 splitPanel.setComponent(other, index); 273 else 274 splitPanel.setComponent(null, index); 275 } 276 } 277 278 public void toggleInputTokensBox() { 279 inputPanel.toggleInputTokensBox(); 280 } 281 282 public boolean isInputTokenVisible() { 283 return inputPanel.isInputTokensBoxVisible(); 284 } 285 286 public void selectConsoleTab() { 287 editor.selectConsoleTab(); 288 } 289 290 public DBOutputPanel getOutputPanel() { 291 return outputPanel; 292 } 293 294 public DBRecorder getRecorder() { 295 return recorder; 296 } 297 298 public DBPlayer getPlayer() { 299 return player; 300 } 301 302 public Container getWindowComponent() { 303 return editor.getWindowContainer(); 304 } 305 306 public EditorConsole getConsole() { 307 return editor.getConsole(); 308 } 309 310 public List <ElementBlock> getBlocks() { 311 return editor.getBlocks(); 312 } 313 314 public EditorProvider getProvider() { 315 return editor; 316 } 317 318 public void close() { 319 debuggerStop(true); 320 inputPanel.close(); 321 parseTreeModel.close(); 322 } 323 324 public Container getContainer() { 325 return panel; 326 } 327 328 public void updateStatusInfo() { 329 controlPanel.updateStatusInfo(); 330 } 331 332 public void breaksOnEvent() { 333 inputPanel.updateOnBreakEvent(); 334 parseTreePanel.updateOnBreakEvent(); 335 astPanel.updateOnBreakEvent(); 336 stackPanel.updateOnBreakEvent(); 337 eventsPanel.updateOnBreakEvent(); 338 } 339 340 public EngineGrammar getGrammar() { 341 return editor.getEngineGrammar(); 342 } 343 344 public boolean needsToGenerateGrammar() { 345 return dateOfModificationOnDisk != editor.getDocument().getDateOfModificationOnDisk() 346 || editor.getDocument().isDirty(); 347 } 348 349 public void grammarGenerated() { 350 editor.getDocument().performAutoSave(); 351 dateOfModificationOnDisk = editor.getDocument().getDateOfModificationOnDisk(); 352 } 353 354 public void queryGrammarBreakpoints() { 355 this.breakpoints = editor.breakpointManager.getBreakpoints(); 356 } 357 358 public boolean isBreakpointAtLine(int line) { 359 if(breakpoints == null) 360 return false; 361 else 362 return breakpoints.contains(Integer.valueOf(line)); 363 } 364 365 public boolean isBreakpointAtToken(Token token) { 366 return inputPanel.isBreakpointAtToken(token); 367 } 368 369 public void selectToken(Token token, int line, int pos) { 370 if(token != null) { 371 374 375 DBInputTextTokenInfo info = inputPanel.getTokenInfoForToken(token); 376 if(info == null) 377 setGrammarPosition(line, pos); 378 else 379 setGrammarPosition(info.line, info.charInLine); 380 } else { 381 382 setGrammarPosition(line, pos); 383 } 384 385 inputPanel.selectToken(token); 386 parseTreePanel.selectToken(token); 387 astPanel.selectToken(token); 388 } 389 390 public int grammarIndex; 391 392 public void setGrammarPosition(int line, int pos) { 393 grammarIndex = computeAbsoluteGrammarIndex(line, pos); 394 if(grammarIndex >= 0) { 395 if(editor.getTextPane().hasFocus()) { 396 SwingUtilities.invokeLater(new Runnable () { 400 public void run() { 401 editor.selectTextRange(grammarIndex, grammarIndex+1); 402 } 403 }); 404 } else 405 editor.selectTextRange(grammarIndex, grammarIndex+1); 406 } 407 } 408 409 public void markLocationInGrammar(int index) { 410 try { 411 editor.setCaretPosition(index); 412 debuggerCursorIndex = index; 413 } catch(Exception e) { 414 getConsole().print(e); 415 } 416 } 417 418 public void resetMarkLocationInGrammar() { 419 debuggerCursorIndex = -1; 420 } 421 422 public int getDebuggerCursorIndex() { 423 return debuggerCursorIndex; 424 } 425 426 public List <ElementRule> getRules() { 427 return editor.getRules(); 428 } 429 430 public List <ElementRule> getSortedRules() { 431 return editor.getSortedRules(); 432 } 433 434 public String getEventsAsString() { 435 return eventsPanel.getEventsAsString(); 436 } 437 438 public int getNumberOfEvents() { 439 return eventsPanel.getNumberOfEvents(); 440 } 441 442 public void launchLocalDebugger(int options) { 443 if(getGrammar().getType() == ElementGrammarName.TREEPARSER) { 445 XJAlert.display(editor.getWindowContainer(), "Unsupported Grammar Type", 446 "ANTLRWorks supports tree grammar debugging only if you \"debug remote\"."); 447 return; 448 } 449 450 if(needsToGenerateGrammar()) { 451 if(AWPrefs.getDebuggerAskGen()) { 452 int result = XJAlert.displayCustomAlert(editor.getWindowContainer(), "Generate and compile", 453 "The grammar has been modified and needs to be generated and compiled again. You can choose " + 454 "to cancel the operation, to continue without generating and compiling the grammar or " + 455 "to generate and compile the grammar.", 456 new String [] { "Cancel", "Continue", "Generate and compile" }, 2); 457 switch(result) { 458 case 0: return; 459 case 2: options = options | OPTION_BUILD; break; 460 461 } 462 } else { 463 options = options | OPTION_BUILD; 464 } 465 } 466 467 if((options & OPTION_BUILD) > 0 && !editor.ensureDocumentSaved()) { 468 return; 469 } 470 471 if((options & OPTION_BUILD) > 0 || !local.isRequiredFilesExisting()) { 472 local.setOutputPath(AWPrefs.getOutputPath()); 473 local.prepareAndLaunch(options); 474 475 grammarGenerated(); 476 } else { 477 local.prepareAndLaunch(options); 478 } 479 } 480 481 public void debuggerLocalDidRun(boolean build) { 482 if(build) 483 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_LOCAL_DEBUGGER_BUILD); 484 else 485 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_LOCAL_DEBUGGER); 486 debuggerLaunch(DEFAULT_LOCAL_ADDRESS, AWPrefs.getDebugDefaultLocalPort(), false); 487 } 488 489 public void launchRemoteDebugger() { 490 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_REMOTE_DEBUGGER); 491 DBRemoteConnectDialog dialog = new DBRemoteConnectDialog(getWindowComponent()); 492 if(dialog.runModal() == XJDialog.BUTTON_OK) { 493 debuggerLaunch(dialog.getAddress(), dialog.getPort(), true); 494 } 495 } 496 497 public void debuggerLaunch(String address, int port, boolean remote) { 498 if(remote && !debuggerLaunchGrammar()) { 499 XJAlert.display(editor.getWindowContainer(), "Error", 500 "Cannot launch the debugger.\nException while parsing grammar."); 501 return; 502 } 503 504 queryGrammarBreakpoints(); 505 506 inputPanel.prepareForGrammar(getGrammar()); 507 player.setInputBuffer(inputPanel.getInputBuffer()); 508 509 recorder.connect(address, port); 510 } 511 512 public void connectionSuccess() { 513 running = true; 516 517 XJNotificationCenter.defaultCenter().postNotification(this, NOTIF_DEBUG_STARTED); 518 editor.selectDebuggerTab(); 519 520 editor.console.makeCurrent(); 521 522 editor.getTextPane().setEditable(false); 523 editor.getTextPane().getCaret().setVisible(false); 524 525 player.resetPlayEvents(true); 526 } 527 528 public void connectionFailed() { 529 XJAlert.display(editor.getWindowContainer(), "Connection Error", 530 "Cannot launch the debugger.\nTime-out waiting to connect to the remote parser."); 531 } 532 533 public void connectionCancelled() { 534 } 535 536 public boolean debuggerLaunchGrammar() { 537 try { 538 getGrammar().analyze(); 539 } catch (Exception e) { 540 editor.getConsole().print(e); 541 return false; 542 } 543 return true; 544 } 545 546 public void debuggerStop(boolean force) { 547 if(recorder.getStatus() == DBRecorder.STATUS_STOPPING) { 548 if(force || XJAlert.displayAlertYESNO(editor.getWindowContainer(), "Stopping", "The debugger is currently stopping. Do you want to force stop it ?") == XJAlert.YES) { 549 local.forceStop(); 550 recorder.stop(); 551 } 552 } else 553 recorder.requestStop(); 554 } 555 556 public boolean isRunning() { 557 return running; 558 } 559 560 public void resetGUI() { 561 stackPanel.clear(); 562 eventsPanel.clear(); 563 parseTreePanel.clear(); 564 astPanel.clear(); 565 } 566 567 public int computeAbsoluteGrammarIndex(int lineIndex, int pos) { 568 List <ATELine> lines = editor.getLines(); 569 if(lineIndex-1<0 || lineIndex-1 >= lines.size()) 570 return -1; 571 572 ATELine line = lines.get(lineIndex-1); 573 return line.position+pos-1; 574 } 575 576 public void addEvent(DBEvent event, DBPlayerContextInfo info) { 577 eventsPanel.addEvent(event, info); 578 } 579 580 public void playEvents(List events, boolean reset) { 581 player.playEvents(events, reset); 582 breaksOnEvent(); 583 } 584 585 public void playerSetLocation(int line, int pos) { 586 parseTreeModel.setLocation(line, pos); 587 } 588 589 public void playerPushRule(String ruleName) { 590 stackPanel.pushRule(ruleName); 591 parseTreeModel.pushRule(ruleName); 592 astModel.pushRule(ruleName); 593 } 594 595 public void playerPopRule(String ruleName) { 596 stackPanel.popRule(); 597 parseTreeModel.popRule(); 598 astModel.popRule(); 599 } 600 601 public void playerConsumeToken(Token token) { 602 parseTreeModel.addToken(token); 603 } 604 605 public void playerRecognitionException(Exception e) { 606 parseTreeModel.addException(e); 607 } 608 609 public void playerBeginBacktrack(int level) { 610 parseTreeModel.beginBacktrack(level); 611 } 612 613 public void playerEndBacktrack(int level, boolean success) { 614 parseTreeModel.endBacktrack(level, success); 615 } 616 617 public void playerNilNode(int id) { 618 astModel.nilNode(id); 619 } 620 621 public void playerCreateNode(int id, Token token) { 622 astModel.createNode(id, token); 623 } 624 625 public void playerCreateNode(int id, String text, int type) { 626 astModel.createNode(id, new ClassicToken(type, text)); 627 } 628 629 public void playerBecomeRoot(int newRootID, int oldRootID) { 630 astModel.becomeRoot(newRootID, oldRootID); 631 } 632 633 public void playerAddChild(int rootID, int childID) { 634 astModel.addChild(rootID, childID); 635 } 636 637 public void playerSetTokenBoundaries(int id, int startIndex, int stopIndex) { 638 639 } 640 641 public void recorderStatusDidChange() { 642 SwingUtilities.invokeLater(new Runnable () { 643 public void run() { 644 updateStatusInfo(); 645 } 646 }); 647 } 648 649 public void recorderDidStop() { 650 SwingUtilities.invokeLater(new Runnable () { 651 public void run() { 652 resetMarkLocationInGrammar(); 653 editor.getTextPane().setEditable(true); 654 editor.getTextPane().requestFocusInWindow(); 655 656 SwingUtilities.invokeLater(new Runnable () { 659 public void run() { 660 editor.getTextPane().getCaret().setVisible(true); 661 } 662 }); 663 664 inputPanel.stop(); 665 running = false; 666 editor.refreshMainMenuBar(); 667 XJNotificationCenter.defaultCenter().postNotification(this, NOTIF_DEBUG_STOPPED); 668 } 669 }); 670 } 671 672 public boolean canExportToBitmap() { 673 return getExportableGView() != null; 674 } 675 676 public boolean canExportToEPS() { 677 return getExportableGView() != null; 678 } 679 680 public GView getExportableGView() { 681 Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); 682 if(Utils.isComponentChildOf(c, parseTreePanel)) 683 return parseTreePanel.getGraphView(); 684 else if(Utils.isComponentChildOf(c, astPanel)) 685 return astPanel.getGraphView(); 686 else 687 return null; 688 } 689 690 public String getTabName() { 691 return "Debugger"; 692 } 693 694 public Component getTabComponent() { 695 return getContainer(); 696 } 697 698 public JPopupMenu treeGetContextualMenu() { 699 ContextualMenuFactory factory = new ContextualMenuFactory(editor.editorMenu); 700 factory.addItem(EditorMenu.MI_EXPORT_AS_EPS); 701 factory.addItem(EditorMenu.MI_EXPORT_AS_IMAGE); 702 return factory.menu; 703 } 704 705 public static final String KEY_SPLITPANE_A = "KEY_SPLITPANE_A"; 706 public static final String KEY_SPLITPANE_B = "KEY_SPLITPANE_B"; 707 public static final String KEY_SPLITPANE_C = "KEY_SPLITPANE_C"; 708 709 public void setPersistentData(Map data) { 710 if(data == null) 711 return; 712 713 724 } 725 726 public Map getPersistentData() { 727 732 return new HashMap (); 733 } 734 735 public void panelDoDetach(DetachablePanel panel) { 736 splitPanel.setComponent(null, panel.getTag()); 737 } 738 739 public void panelDoAttach(DetachablePanel panel) { 740 Component c = splitPanel.getComponentAtIndex(panel.getTag()); 741 if(c != null) { 742 c.setVisible(false); 743 splitPanel.setComponent(null, panel.getTag()); 744 745 CustomToggleButton button = components2toggle.get(c); 746 button.setSelected(false); 747 } 748 splitPanel.setComponent(panel, panel.getTag()); 749 } 750 751 public void panelDoClose(DetachablePanel panel) { 752 CustomToggleButton button = components2toggle.get(panel); 753 button.setSelected(false); 754 } 755 756 public Container panelParentContainer() { 757 return editor.getJavaContainer(); 758 } 759 760 public boolean canDebugAgain() { 761 return local.canDebugAgain(); 762 } 763 764 public void warning(Object o, String message) { 765 getConsole().println("["+o.getClass().getName()+" - event "+getNumberOfEvents()+"] Warning: "+message, Console.LEVEL_WARNING); 766 } 767 } 768 | Popular Tags |