001 package nl.cwi.sen1.tide.tool.ruleinspector; 002 003 //{{{ imports 004 005 import java.awt.BorderLayout; 006 import java.awt.Color; 007 import java.awt.GridLayout; 008 import java.awt.event.ActionEvent; 009 import java.awt.event.ActionListener; 010 import java.awt.event.InputMethodEvent; 011 import java.awt.event.InputMethodListener; 012 import java.util.ArrayList; 013 import java.util.Iterator; 014 import java.util.List; 015 016 import javax.swing.JButton; 017 import javax.swing.JCheckBox; 018 import javax.swing.JComboBox; 019 import javax.swing.JLabel; 020 import javax.swing.JOptionPane; 021 import javax.swing.JPanel; 022 import javax.swing.JScrollPane; 023 import javax.swing.JTable; 024 import javax.swing.JTextArea; 025 import javax.swing.JTextField; 026 import javax.swing.ListSelectionModel; 027 import javax.swing.SwingConstants; 028 import javax.swing.border.LineBorder; 029 import javax.swing.border.TitledBorder; 030 import javax.swing.event.DocumentEvent; 031 import javax.swing.event.DocumentListener; 032 import javax.swing.event.ListSelectionEvent; 033 import javax.swing.event.ListSelectionListener; 034 import javax.swing.event.TableModelEvent; 035 import javax.swing.table.AbstractTableModel; 036 import javax.swing.table.DefaultTableCellRenderer; 037 import javax.swing.table.TableColumn; 038 039 import nl.cwi.sen1.tide.tool.ProcessTool; 040 import nl.cwi.sen1.tide.tool.ToolManager; 041 import nl.cwi.sen1.tide.tool.support.DebugAdapter; 042 import nl.cwi.sen1.tide.tool.support.DebugAdapterListener; 043 import nl.cwi.sen1.tide.tool.support.DebugProcess; 044 import nl.cwi.sen1.tide.tool.support.DebugProcessListener; 045 import nl.cwi.sen1.tide.tool.support.Expr; 046 import nl.cwi.sen1.tide.tool.support.Port; 047 import nl.cwi.sen1.tide.tool.support.Rule; 048 049 //}}} 050 051 public class RuleInspector 052 extends ProcessTool 053 implements DebugAdapterListener 054 { 055 private RuleSelector selector; 056 private RuleEditor editor; 057 058 //{{{ public RuleInspector(ToolManager manager, final DebugProcess process) 059 060 public RuleInspector(ToolManager manager, final DebugProcess process) 061 { 062 super(manager, process); 063 064 setSize(360, 350); 065 066 setLayout(new GridLayout(0,1)); 067 068 selector = new RuleSelector(process); 069 editor = new RuleEditor(process); 070 selector.addRuleSelectionListener(editor); 071 072 add(selector); 073 add(editor); 074 075 selector.setBackground(Color.white); 076 editor.setBackground(Color.white); 077 078 DebugAdapter adapter = process.getAdapter(); 079 adapter.addDebugAdapterListener(this); 080 } 081 082 //}}} 083 //{{{ public void editRule(Rule rule) 084 085 public void editRule(Rule rule) 086 { 087 selector.selectRule(rule); 088 } 089 090 //}}} 091 092 //{{{ public void processDestroyed(DebugAdapter adapter, DebugProcess proc) 093 094 public void processDestroyed(DebugAdapter adapter, DebugProcess proc) 095 { 096 if (proc == process) { 097 destroy(); 098 } 099 } 100 101 //}}} 102 //{{{ public void processCreated(DebugAdapter adapter, DebugProcess proc) 103 104 public void processCreated(DebugAdapter adapter, DebugProcess proc) 105 { 106 } 107 108 //}}} 109 } 110 111 class RuleSelector 112 extends JPanel 113 implements DebugProcessListener, ListSelectionListener 114 { 115 private DebugProcess process; 116 117 List<RuleSelectionListener> listeners; 118 119 RuleSelectorTableModel tableModel; 120 JTable table; 121 122 //{{{ public RuleSelector(DebugProcess process) 123 124 public RuleSelector(DebugProcess process) 125 { 126 this.process = process; 127 listeners = new ArrayList<RuleSelectionListener>(); 128 setBorder(new TitledBorder(new LineBorder(Color.gray), "Rule Selector")); 129 130 tableModel = new RuleSelectorTableModel(process); 131 table = new JTable(tableModel); 132 133 ListSelectionModel selModel = table.getSelectionModel(); 134 selModel.addListSelectionListener(this); 135 136 TableColumn column_id = table.getColumn(tableModel.getColumnName(0)); 137 column_id.setMaxWidth(30); 138 DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); 139 renderer.setHorizontalAlignment(SwingConstants.CENTER); 140 column_id.setCellRenderer(renderer); 141 142 setLayout(new BorderLayout()); 143 add(new JScrollPane(table), "Center"); 144 145 process.addDebugProcessListener(this); 146 } 147 148 //}}} 149 //{{{ public void cleanup() 150 151 public void cleanup() 152 { 153 process.removeDebugProcessListener(this); 154 } 155 156 //}}} 157 //{{{ public void addRuleSelectionListener(RuleSelectionListener listener) 158 159 public void addRuleSelectionListener(RuleSelectionListener listener) 160 { 161 listeners.add(listener); 162 } 163 164 //}}} 165 //{{{ private void fireRuleSelected(Rule rule) 166 167 private void fireRuleSelected(Rule rule) 168 { 169 Iterator<RuleSelectionListener> iter = listeners.iterator(); 170 while (iter.hasNext()) { 171 RuleSelectionListener listener = iter.next(); 172 listener.ruleSelected(rule); 173 } 174 } 175 176 //}}} 177 //{{{ public void ruleCreated(DebugProcess process, Rule rule) 178 179 public void ruleCreated(DebugProcess process, Rule rule) 180 { 181 int index = process.getNrOfRules()-1; 182 tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index, 183 TableModelEvent.ALL_COLUMNS, 184 TableModelEvent.INSERT)); 185 } 186 187 //}}} 188 //{{{ public void ruleDeleted(DebugProcess process, Rule rule) 189 190 public void ruleDeleted(DebugProcess process, Rule rule) 191 { 192 int index = process.getRuleIndex(rule); 193 tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index, 194 TableModelEvent.ALL_COLUMNS, 195 TableModelEvent.DELETE)); 196 } 197 198 //}}} 199 //{{{ public void ruleModified(DebugProcess process, Rule rule) 200 201 public void ruleModified(DebugProcess process, Rule rule) 202 { 203 int index = process.getRuleIndex(rule); 204 tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index, 205 TableModelEvent.ALL_COLUMNS, 206 TableModelEvent.UPDATE)); 207 } 208 209 //}}} 210 //{{{ public void ruleTriggered(DebugProcess process, Rule rule, Expr value) 211 212 public void ruleTriggered(DebugProcess process, Rule rule, Expr value){} 213 214 //}}} 215 //{{{ public void evaluationResult(process, expr, value, tag) 216 217 public void evaluationResult(DebugProcess process, Expr expr, Expr value, String tag){} 218 219 //}}} 220 221 //{{{ void selectRule(Rule rule) 222 223 void selectRule(Rule rule) 224 { 225 int index = process.getRuleIndex(rule); 226 table.changeSelection(index, 0, false, false); 227 fireRuleSelected(rule); 228 } 229 230 //}}} 231 232 //{{{ public void valueChanged(ListSelectionEvent event) 233 234 public void valueChanged(ListSelectionEvent event) 235 { 236 int row = table.getSelectedRow(); 237 238 if (row != -1) { 239 Rule rule = process.getRuleAt(row); 240 fireRuleSelected(rule); 241 } 242 } 243 244 //}}} 245 } 246 247 class RuleSelectorTableModel 248 extends AbstractTableModel 249 { 250 private DebugProcess process; 251 252 //{{{ public RuleSelectorTableModel(DebugProcess process) 253 254 public RuleSelectorTableModel(DebugProcess process) 255 { 256 this.process = process; 257 } 258 259 //}}} 260 //{{{ public int getRowCount() 261 262 public int getRowCount() 263 { 264 return process.getNrOfRules(); 265 } 266 267 //}}} 268 //{{{ public int getColumnCount() 269 270 public int getColumnCount() 271 { 272 return 3; 273 } 274 275 //}}} 276 //{{{ public Object getValueAt(int row, int col) 277 278 public Object getValueAt(int row, int col) 279 { 280 Rule rule = process.getRuleAt(row); 281 switch (col) { 282 case 0: 283 return new Integer(rule.getRid()); 284 case 1: 285 return rule.getTag(); 286 case 2: 287 return rule.getPort().toTerm(); 288 } 289 290 throw new RuntimeException("illegal column: " + col); 291 } 292 293 //}}} 294 //{{{ public String getColumnName(int col) 295 296 public String getColumnName(int col) 297 { 298 switch (col) { 299 case 0: return "Id"; 300 case 1: return "Tag"; 301 case 2: return "Port"; 302 } 303 throw new RuntimeException("illegal column: " + col); 304 } 305 306 //}}} 307 //{{{ public Class getColumnClass(int col) 308 309 public Class<?> getColumnClass(int col) 310 { 311 return getValueAt(0, col).getClass(); 312 } 313 314 //}}} 315 } 316 317 class RuleEditor 318 extends JPanel 319 implements RuleSelectionListener, ActionListener, DebugProcessListener 320 { 321 private final static Color COLOR_MODIFIED = Color.red; 322 private final static Color COLOR_UNMODIFIED = Color.black; 323 324 DebugProcess process; 325 Rule rule; 326 TitledBorder border; 327 328 boolean modified; 329 330 //{{{ UI attributes 331 332 JLabel tag; 333 JCheckBox enabled; 334 JLabel portLabel; 335 JComboBox port; 336 JLabel conditionLabel; 337 JTextField condition; 338 JLabel actionLabel; 339 JTextArea action; 340 341 JButton createButton; 342 JButton applyButton; 343 JButton revertButton; 344 JButton deleteButton; 345 346 //}}} 347 348 //{{{ public RuleEditor(DebugProcess process) 349 350 public RuleEditor(DebugProcess process) 351 { 352 this.process = process; 353 354 //{{{ Create UI components 355 356 border = new TitledBorder(new LineBorder(Color.gray), "Rule Editor"); 357 setBorder(border); 358 359 tag = new JLabel(); 360 tag.setForeground(COLOR_UNMODIFIED); 361 enabled = new JCheckBox("Enabled"); 362 portLabel = new JLabel("Port:"); 363 port = new JComboBox(); 364 port.setEnabled(false); 365 366 for (int i=0; i<DebugProcess.PORT_TYPES.length; i++) { 367 port.addItem(DebugProcess.PORT_TYPES[i]); 368 } 369 370 conditionLabel = new JLabel("Condition:"); 371 condition = new JTextField(); 372 actionLabel = new JLabel("Action:", SwingConstants.CENTER); 373 action = new JTextArea(); 374 createButton = new JButton("Create"); 375 applyButton = new JButton("Apply"); 376 revertButton = new JButton("Revert"); 377 deleteButton = new JButton("Delete"); 378 379 380 //}}} 381 382 //{{{ Add event listeners 383 384 process.addDebugProcessListener(this); 385 386 enabled.addActionListener(this); 387 createButton.addActionListener(this); 388 applyButton.addActionListener(this); 389 revertButton.addActionListener(this); 390 deleteButton.addActionListener(this); 391 port.addActionListener(this); 392 condition.addActionListener(this); 393 394 condition.getDocument().addDocumentListener(new DocumentListener() 395 { 396 public void changedUpdate(DocumentEvent evt) { 397 conditionLabel.setForeground(COLOR_MODIFIED); 398 setModified(true); 399 } 400 public void insertUpdate(DocumentEvent evt) { 401 conditionLabel.setForeground(COLOR_MODIFIED); 402 setModified(true); 403 } 404 public void removeUpdate(DocumentEvent evt) { 405 conditionLabel.setForeground(COLOR_MODIFIED); 406 setModified(true); 407 } 408 }); 409 410 action.addInputMethodListener(new InputMethodListener() 411 { 412 public void caretPositionChanged(InputMethodEvent evt) { } 413 public void inputMethodTextChanged(InputMethodEvent evt) { 414 actionLabel.setForeground(COLOR_MODIFIED); 415 } 416 }); 417 418 //}}} 419 420 //{{{ Build UI 421 422 setLayout(new BorderLayout()); 423 JPanel top = new JPanel(); 424 top.setLayout(new BorderLayout()); 425 JPanel labels = new JPanel(); 426 labels.setLayout(new GridLayout(3,1)); 427 428 JLabel tagLabel = new JLabel("Tag:"); 429 tagLabel.setForeground(COLOR_UNMODIFIED); 430 labels.add(tagLabel); 431 labels.add(portLabel); 432 labels.add(conditionLabel); 433 top.add("West", labels); 434 435 JPanel controls = new JPanel(); 436 controls.setLayout(new GridLayout(3,1)); 437 438 JPanel tagAndEnabling = new JPanel(); 439 tagAndEnabling.setLayout(new BorderLayout()); 440 tagAndEnabling.add("West", tag); 441 tagAndEnabling.add("East", enabled); 442 controls.add(tagAndEnabling); 443 controls.add(port); 444 controls.add(condition); 445 top.add("Center", controls); 446 447 JPanel bottom = new JPanel(); 448 bottom.setLayout(new BorderLayout()); 449 bottom.add(actionLabel, "North"); 450 bottom.add(action, "Center"); 451 452 JPanel buttons = new JPanel(); 453 buttons.setLayout(new GridLayout(1,0)); 454 buttons.add(createButton); 455 buttons.add(applyButton); 456 buttons.add(revertButton); 457 buttons.add(deleteButton); 458 459 add("North", top); 460 add("Center", bottom); 461 add("South", buttons); 462 463 464 //}}} 465 466 clear(); 467 } 468 469 //}}} 470 //{{{ public void cleanup() 471 472 public void cleanup() 473 { 474 process.removeDebugProcessListener(this); 475 } 476 477 //}}} 478 //{{{ public void clear() 479 480 public void clear() 481 { 482 tag.setText("-"); 483 enabled.setSelected(false); 484 enabled.setEnabled(false); 485 port.setSelectedIndex(0); 486 port.setEnabled(false); 487 condition.setText(""); 488 condition.setEnabled(false); 489 action.setText(""); 490 action.setEnabled(false); 491 492 493 applyButton.setEnabled(false); 494 revertButton.setEnabled(false); 495 deleteButton.setEnabled(false); 496 497 enabled.setForeground(COLOR_UNMODIFIED); 498 portLabel.setForeground(COLOR_UNMODIFIED); 499 conditionLabel.setForeground(COLOR_UNMODIFIED); 500 actionLabel.setForeground(COLOR_UNMODIFIED); 501 } 502 503 //}}} 504 //{{{ public void ruleSelected(Rule rule) 505 506 public void ruleSelected(Rule rule) 507 { 508 this.rule = rule; 509 border.setTitle("Rule Editor: Rule " + rule.getRid()); 510 511 tag.setText(rule.getTag()); 512 enabled.setSelected(rule.isEnabled()); 513 enabled.setEnabled(true); 514 port.setSelectedItem(rule.getPort().toTerm().toString()); 515 port.setEnabled(true); 516 condition.setText(rule.getCondition().toTerm().toString()); 517 condition.setEnabled(true); 518 action.setText(rule.getAction().toTerm().toString()); 519 action.setEnabled(true); 520 521 applyButton.setEnabled(false); 522 revertButton.setEnabled(false); 523 deleteButton.setEnabled(true); 524 525 enabled.setForeground(COLOR_UNMODIFIED); 526 portLabel.setForeground(COLOR_UNMODIFIED); 527 conditionLabel.setForeground(COLOR_UNMODIFIED); 528 actionLabel.setForeground(COLOR_UNMODIFIED); 529 530 setModified(false); 531 532 533 repaint(); 534 } 535 536 //}}} 537 //{{{ public void ruleCreated(DebugProcess process, Rule rule) 538 539 public void ruleCreated(DebugProcess process, Rule rule){} 540 541 //}}} 542 //{{{ public void ruleDeleted(DebugProcess process, Rule rule) 543 544 public void ruleDeleted(DebugProcess process, Rule rule) 545 { 546 if (rule == this.rule) { 547 clear(); 548 rule = null; 549 } 550 } 551 552 //}}} 553 //{{{ public void ruleModified(DebugProcess process, Rule rule) 554 555 public void ruleModified(DebugProcess process, Rule rule) 556 { 557 if (rule == this.rule) { 558 ruleSelected(rule); 559 } 560 } 561 562 //}}} 563 //{{{ public void ruleTriggered(DebugProcess process, Rule rule, Expr value) 564 565 public void ruleTriggered(DebugProcess process, Rule rule, Expr value){} 566 567 //}}} 568 //{{{ public void evaluationResult(process, expr, value, tag) 569 570 public void evaluationResult(DebugProcess process, Expr expr, Expr value, String tag){} 571 572 //}}} 573 //{{{ private void setModified(boolean on) 574 575 private void setModified(boolean on) 576 { 577 modified = on; 578 revertButton.setEnabled(on); 579 applyButton.setEnabled(on); 580 } 581 582 //}}} 583 584 //{{{ public void actionPerformed(ActionEvent event) 585 586 public void actionPerformed(ActionEvent event) 587 { 588 Object source = event.getSource(); 589 590 if (source == enabled) { 591 //{{{ Change enabled button to modified 592 593 enabled.setForeground(COLOR_MODIFIED); 594 setModified(true); 595 596 //}}} 597 } else if (source == port) { 598 //{{{ Change port combobox to modified 599 600 if (port.isEnabled()) { 601 portLabel.setForeground(COLOR_MODIFIED); 602 setModified(true); 603 } 604 605 //}}} 606 } else if (source == createButton) { 607 //{{{ Handle create rule 608 609 String tag = (String) 610 JOptionPane.showInternalInputDialog(this, 611 "What tag should the new rule have?", 612 "Create a new Rule", 613 JOptionPane.PLAIN_MESSAGE, 614 null, null, "manual-rule"); 615 if (tag != null) { 616 process.requestRuleCreation(Port.makeStep(), Expr.makeTrue(), Expr.makeTrue(), 617 tag, false); 618 } 619 620 //}}} 621 } else if (source == revertButton) { 622 //{{{ Handle revert rule 623 624 ruleSelected(rule); 625 626 //}}} 627 } else if (source == applyButton) { 628 //{{{ Handle apply changes 629 630 String portItem = (String)port.getSelectedItem(); 631 Port port = Port.parse(portItem); 632 if (port == null) { 633 JOptionPane.showInternalMessageDialog(this, "Illegal port: " + portItem, 634 "Illegal Port", 635 JOptionPane.ERROR_MESSAGE); 636 return; 637 } 638 639 String condTxt = condition.getText(); 640 Expr cond = Expr.parse(condTxt); 641 if (cond == null) { 642 JOptionPane.showInternalMessageDialog(this, "Not an expression: " + condTxt, 643 "Illegal Condition", 644 JOptionPane.ERROR_MESSAGE); 645 return; 646 } 647 648 String actTxt = action.getText(); 649 Expr act = Expr.parse(actTxt); 650 if (act == null) { 651 JOptionPane.showInternalMessageDialog(this, "Not an expression: " + actTxt, 652 "Illegal Action", 653 JOptionPane.ERROR_MESSAGE); 654 return; 655 } 656 657 boolean enabled = this.enabled.isSelected(); 658 659 process.requestRuleModification(rule, port, cond, act, enabled); 660 661 //}}} 662 } else if (source == deleteButton) { 663 //{{{ Handle delete rule 664 665 process.requestRuleDeletion(rule); 666 667 //}}} 668 } 669 } 670 671 //}}} 672 } 673 674 //{{{ interface RuleSelectionListener 675 676 interface RuleSelectionListener 677 { 678 public void ruleSelected(Rule rule); 679 } 680 681 //}}} 682