1 /* 2 [The "BSD licence"] 3 Copyright (c) 2009 Shaoting Cai 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 package org.antlr.gunit.swingui; 30 31 import org.antlr.gunit.swingui.model.*; 32 import org.antlr.gunit.swingui.ImageFactory; 33 import java.awt.*; 34 import java.awt.event.*; 35 import java.util.HashMap; 36 import javax.swing.*; 37 import javax.swing.event.*; 38 39 /** 40 * 41 * @author scai 42 */ 43 public class TestCaseEditController implements IController { 44 45 private JPanel view = new JPanel(); 46 47 private JScrollPane scroll; 48 private JPanel paneDetail; 49 private AbstractEditorPane paneDetailInput, paneDetailOutput; 50 private JToolBar toolbar; 51 private JList listCases; 52 private ListModel listModel ; 53 54 public ActionListener onTestCaseNumberChange; 55 56 /* EDITORS */ 57 private InputFileEditor editInputFile; 58 private InputStringEditor editInputString; 59 private InputMultiEditor editInputMulti; 60 private OutputResultEditor editOutputResult; 61 private OutputAstEditor editOutputAST; 62 private OutputStdEditor editOutputStd; 63 private OutputReturnEditor editOutputReturn; 64 65 private JComboBox comboInputType, comboOutputType; 66 67 /* TYPE NAME */ 68 private static final String IN_TYPE_STRING = "Single-line Text"; 69 private static final String IN_TYPE_MULTI = "Multi-line Text"; 70 private static final String IN_TYPE_FILE = "Disk File"; 71 private static final String OUT_TYPE_BOOL = "OK or Fail"; 72 private static final String OUT_TYPE_AST = "AST"; 73 private static final String OUT_TYPE_STD = "Standard Output"; 74 private static final String OUT_TYPE_RET = "Return Value"; 75 76 private static final String DEFAULT_IN_SCRIPT = ""; 77 private static final String DEFAULT_OUT_SCRIPT = ""; 78 79 private static final Object[] INPUT_TYPE = { 80 IN_TYPE_STRING, IN_TYPE_MULTI, IN_TYPE_FILE 81 }; 82 83 private static final Object[] OUTPUT_TYPE = { 84 OUT_TYPE_BOOL, OUT_TYPE_AST, OUT_TYPE_STD, OUT_TYPE_RET 85 }; 86 87 /* SIZE */ 88 private static final int TEST_CASE_DETAIL_WIDTH = 300; 89 private static final int TEST_EDITOR_WIDTH = 280; 90 private static final int TEST_CASE_DETAIL_HEIGHT = 250; 91 private static final int TEST_EDITOR_HEIGHT = 120; 92 93 /* MODEL */ 94 private Rule currentRule = null; 95 private TestCase currentTestCase = null; 96 97 /* END OF MODEL*/ 98 99 private static final HashMap<Class<?>, String> TypeNameTable; 100 static { 101 TypeNameTable = new HashMap<Class<?>, String> (); 102 TypeNameTable.put(TestCaseInputString.class, IN_TYPE_STRING); 103 TypeNameTable.put(TestCaseInputMultiString.class, IN_TYPE_MULTI); 104 TypeNameTable.put(TestCaseInputFile.class, IN_TYPE_FILE); 105 106 TypeNameTable.put(TestCaseOutputResult.class, OUT_TYPE_BOOL); 107 TypeNameTable.put(TestCaseOutputAST.class, OUT_TYPE_AST); 108 TypeNameTable.put(TestCaseOutputStdOut.class, OUT_TYPE_STD); 109 TypeNameTable.put(TestCaseOutputReturn.class, OUT_TYPE_RET); 110 } 111 112 //private WorkSpaceView owner; 113 114 public TestCaseEditController(WorkSpaceView workspace) { 115 //this.owner = workspace; 116 initComponents(); 117 } 118 119 public TestCaseEditController() { 120 initComponents(); 121 } 122 123 public void OnLoadRule(Rule rule) { 124 if(rule == null) throw new IllegalArgumentException("Null"); 125 this.currentRule = rule; 126 this.currentTestCase = null; 127 this.listModel = rule; 128 this.listCases.setModel(this.listModel); 129 } 130 131 public void setCurrentTestCase(TestCase testCase) { 132 if(testCase == null) throw new IllegalArgumentException("Null"); 133 this.listCases.setSelectedValue(testCase, true); 134 this.currentTestCase = testCase; 135 } 136 137 public Rule getCurrentRule() { 138 return this.currentRule; 139 } 140 141 private void initComponents() { 142 143 /* CASE LIST */ 144 listCases = new JList(); 145 listCases.addListSelectionListener(new TestCaseListSelectionListener()); 146 listCases.setCellRenderer(listRenderer); 147 listCases.setOpaque(false); 148 149 scroll = new JScrollPane(listCases); 150 scroll.setBorder(BorderFactory.createTitledBorder( 151 BorderFactory.createEmptyBorder(), "Test Cases")); 152 scroll.setOpaque(false); 153 scroll.setViewportBorder(BorderFactory.createEtchedBorder()); 154 155 /* CASE DETAIL */ 156 157 editInputString = new InputStringEditor(); 158 editInputMulti = new InputMultiEditor(); 159 editInputFile = new InputFileEditor(); 160 161 editOutputResult = new OutputResultEditor(); 162 editOutputAST = new OutputAstEditor(); 163 editOutputStd = new OutputStdEditor(); 164 editOutputReturn = new OutputReturnEditor(); 165 166 paneDetail = new JPanel(); 167 paneDetail.setBorder(BorderFactory.createEmptyBorder()); 168 paneDetail.setOpaque(false); 169 170 comboInputType = new JComboBox(INPUT_TYPE); 171 comboInputType.addActionListener(new ActionListener() { 172 public void actionPerformed(ActionEvent event) { 173 OnInputTestCaseTypeChanged(comboInputType.getSelectedItem()); 174 } 175 }); 176 comboOutputType = new JComboBox(OUTPUT_TYPE); 177 comboOutputType.addActionListener(new ActionListener() { 178 public void actionPerformed(ActionEvent event) { 179 OnOutputTestCaseTypeChanged(comboOutputType.getSelectedItem()); 180 } 181 }); 182 paneDetailInput = new InputEditorPane(comboInputType); 183 paneDetailOutput = new OutputEditorPane(comboOutputType); 184 185 BoxLayout layout = new BoxLayout(paneDetail, BoxLayout.PAGE_AXIS); 186 paneDetail.setLayout(layout); 187 188 paneDetail.add(this.paneDetailInput); 189 paneDetail.add(this.paneDetailOutput); 190 191 /* TOOLBAR */ 192 toolbar = new JToolBar("Edit TestCases", JToolBar.VERTICAL); 193 toolbar.setFloatable(false); 194 toolbar.add(new AddTestCaseAction()); 195 toolbar.add(new RemoveTestCaseAction()); 196 197 /* COMPOSITE */ 198 view.setLayout(new BorderLayout()); 199 view.setBorder(BorderFactory.createEmptyBorder()); 200 view.setOpaque(false); 201 view.add(toolbar, BorderLayout.WEST); 202 view.add(scroll, BorderLayout.CENTER); 203 view.add(paneDetail, BorderLayout.EAST); 204 } 205 206 private void updateInputEditor() { 207 JComponent editor = null; 208 209 if(currentTestCase != null ) { 210 ITestCaseInput input = this.currentTestCase.getInput(); 211 if(input instanceof TestCaseInputString) { 212 this.editInputString.setText(input.getScript()); 213 editor = this.editInputString; 214 comboInputType.setSelectedItem(IN_TYPE_STRING); 215 } else if(input instanceof TestCaseInputMultiString) { 216 this.editInputMulti.setText(input.getScript()); 217 editor = this.editInputMulti.getView(); 218 comboInputType.setSelectedItem(IN_TYPE_MULTI); 219 } else if(input instanceof TestCaseInputFile) { 220 this.editInputFile.setText(input.getScript()); 221 editor = this.editInputFile; 222 comboInputType.setSelectedItem(IN_TYPE_FILE); 223 } else { 224 throw new Error("Wrong type"); 225 } 226 } 227 228 paneDetailInput.setEditor(editor); 229 } 230 231 private void updateOutputEditor() { 232 JComponent editor = null; 233 234 if(currentTestCase != null) { 235 236 ITestCaseOutput output = this.currentTestCase.getOutput(); 237 238 if(output instanceof TestCaseOutputAST) { 239 240 this.editOutputAST.setText(output.getScript()); 241 editor = this.editOutputAST.getView(); 242 comboOutputType.setSelectedItem(OUT_TYPE_AST); 243 244 } else if(output instanceof TestCaseOutputResult) { 245 246 this.editOutputResult.setValue(output.getScript()); 247 editor = this.editOutputResult; 248 comboOutputType.setSelectedItem(OUT_TYPE_BOOL); 249 250 } else if(output instanceof TestCaseOutputStdOut) { 251 252 this.editOutputStd.setText(output.getScript()); 253 editor = this.editOutputStd.getView(); 254 comboOutputType.setSelectedItem(OUT_TYPE_STD); 255 256 } else if(output instanceof TestCaseOutputReturn) { 257 258 this.editOutputReturn.setText(output.getScript()); 259 editor = this.editOutputReturn.getView(); 260 comboOutputType.setSelectedItem(OUT_TYPE_RET); 261 262 } else { 263 264 throw new Error("Wrong type"); 265 266 } 267 268 } 269 this.paneDetailOutput.setEditor(editor); 270 } 271 272 private void OnInputTestCaseTypeChanged(Object inputTypeStr) { 273 if(this.currentTestCase != null) { 274 ITestCaseInput input ; 275 if(inputTypeStr == IN_TYPE_STRING) { 276 input = new TestCaseInputString(DEFAULT_IN_SCRIPT); 277 } else if(inputTypeStr == IN_TYPE_MULTI) { 278 input = new TestCaseInputMultiString(DEFAULT_IN_SCRIPT); 279 } else if(inputTypeStr == IN_TYPE_FILE) { 280 input = new TestCaseInputFile(DEFAULT_IN_SCRIPT); 281 } else { 282 throw new Error("Wrong Type"); 283 } 284 285 if(input.getClass().equals(this.currentTestCase.getInput().getClass())) 286 return ; 287 288 this.currentTestCase.setInput(input); 289 } 290 this.updateInputEditor(); 291 } 292 293 private void OnOutputTestCaseTypeChanged(Object outputTypeStr) { 294 if(this.currentTestCase != null) { 295 296 ITestCaseOutput output ; 297 if(outputTypeStr == OUT_TYPE_AST) { 298 output = new TestCaseOutputAST(DEFAULT_OUT_SCRIPT); 299 } else if(outputTypeStr == OUT_TYPE_BOOL) { 300 output = new TestCaseOutputResult(false); 301 } else if(outputTypeStr == OUT_TYPE_STD) { 302 output = new TestCaseOutputStdOut(DEFAULT_OUT_SCRIPT); 303 } else if(outputTypeStr == OUT_TYPE_RET) { 304 output = new TestCaseOutputReturn(DEFAULT_OUT_SCRIPT); 305 } else { 306 throw new Error("Wrong Type"); 307 } 308 309 if(output.getClass().equals(this.currentTestCase.getOutput().getClass())) 310 return ; 311 312 this.currentTestCase.setOutput(output); 313 } 314 this.updateOutputEditor(); 315 } 316 317 318 private void OnTestCaseSelected(TestCase testCase) { 319 //if(testCase == null) throw new RuntimeException("Null TestCase"); 320 this.currentTestCase = testCase; 321 updateInputEditor(); 322 updateOutputEditor(); 323 324 } 325 326 private void OnAddTestCase() { 327 if(currentRule == null) return; 328 329 final TestCase newCase = new TestCase( 330 new TestCaseInputString(""), 331 new TestCaseOutputResult(true)); 332 this.currentRule.addTestCase(newCase); 333 setCurrentTestCase(newCase); 334 335 this.listCases.setSelectedValue(newCase, true); 336 this.listCases.updateUI(); 337 this.OnTestCaseSelected(newCase); 338 this.onTestCaseNumberChange.actionPerformed(null); 339 } 340 341 private void OnRemoveTestCase() { 342 if(currentTestCase == null) return; 343 currentRule.removeElement(currentTestCase); 344 listCases.updateUI(); 345 346 final TestCase nextActiveCase = listCases.isSelectionEmpty() ? 347 null : (TestCase) listCases.getSelectedValue() ; 348 OnTestCaseSelected(nextActiveCase); 349 this.onTestCaseNumberChange.actionPerformed(null); 350 } 351 352 public Object getModel() { 353 return currentRule; 354 } 355 356 public Component getView() { 357 return view; 358 } 359 360 /* EDITOR CONTAINER */ 361 362 abstract public class AbstractEditorPane extends JPanel { 363 364 private JComboBox combo; 365 private JComponent editor; 366 private String title; 367 private JLabel placeHolder = new JLabel(); 368 369 public AbstractEditorPane(JComboBox comboBox, String title) { 370 this.combo = comboBox; 371 this.editor = placeHolder; 372 this.title = title; 373 this.initComponents(); 374 } 375 376 private void initComponents() { 377 placeHolder.setPreferredSize(new Dimension( 378 TEST_CASE_DETAIL_WIDTH, TEST_CASE_DETAIL_HEIGHT)); 379 this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 380 this.add(combo, BorderLayout.NORTH); 381 this.add(editor, BorderLayout.CENTER); 382 this.setOpaque(false); 383 this.setBorder(BorderFactory.createTitledBorder(title)); 384 this.setPreferredSize(new Dimension( 385 TEST_CASE_DETAIL_WIDTH, TEST_CASE_DETAIL_HEIGHT)); 386 } 387 388 public void setEditor(JComponent newEditor) { 389 if(newEditor == null) newEditor = placeHolder; 390 this.remove(editor); 391 this.add(newEditor); 392 this.editor = newEditor; 393 this.updateUI(); 394 } 395 } 396 397 public class InputEditorPane extends AbstractEditorPane { 398 public InputEditorPane(JComboBox comboBox) { 399 super(comboBox, "Input"); 400 } 401 } 402 403 public class OutputEditorPane extends AbstractEditorPane { 404 public OutputEditorPane(JComboBox comboBox) { 405 super(comboBox, "Output"); 406 } 407 } 408 409 /* INPUT EDITORS */ 410 411 public class InputStringEditor extends JTextField implements CaretListener { 412 public InputStringEditor() { 413 super(); 414 415 this.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); 416 this.addCaretListener(this); 417 } 418 419 public void caretUpdate(CaretEvent arg0) { 420 currentTestCase.getInput().setScript(getText()); 421 listCases.updateUI(); 422 } 423 } 424 425 public class InputMultiEditor implements CaretListener { 426 private JTextArea textArea = new JTextArea(20, 30); 427 private JScrollPane scroll = new JScrollPane(textArea, 428 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 429 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 430 431 public InputMultiEditor() { 432 super(); 433 scroll.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); 434 textArea.addCaretListener(this); 435 } 436 437 public void caretUpdate(CaretEvent arg0) { 438 currentTestCase.getInput().setScript(getText()); 439 listCases.updateUI(); 440 } 441 442 public String getText() { 443 return textArea.getText(); 444 } 445 446 public void setText(String text) { 447 textArea.setText(text); 448 } 449 450 public JComponent getView() { 451 return scroll; 452 } 453 } 454 455 public class InputFileEditor extends InputStringEditor {}; 456 457 public class OutputResultEditor extends JPanel implements ActionListener { 458 459 private JToggleButton tbFail, tbOk; 460 461 public OutputResultEditor() { 462 super(); 463 464 tbFail = new JToggleButton("Fail"); 465 tbOk = new JToggleButton("OK"); 466 ButtonGroup group = new ButtonGroup(); 467 group.add(tbFail); 468 group.add(tbOk); 469 470 this.add(tbFail); 471 this.add(tbOk); 472 473 this.tbFail.addActionListener(this); 474 this.tbOk.addActionListener(this); 475 476 this.setPreferredSize( 477 new Dimension(TEST_EDITOR_WIDTH, 100)); 478 } 479 480 public void actionPerformed(ActionEvent e) { 481 TestCaseOutputResult output = 482 (TestCaseOutputResult) currentTestCase.getOutput(); 483 484 if(e.getSource() == tbFail) { 485 output.setScript(false); 486 } else { 487 output.setScript(true); 488 } 489 490 listCases.updateUI(); 491 } 492 493 public void setValue(String value) { 494 if(TestCaseOutputResult.OK.equals(value)) { 495 this.tbOk.setSelected(true); 496 } else { 497 this.tbFail.setSelected(true); 498 } 499 } 500 } 501 502 503 public class OutputAstEditor implements CaretListener { 504 private JTextArea textArea = new JTextArea(20, 30); 505 private JScrollPane scroll = new JScrollPane(textArea, 506 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 507 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 508 509 public OutputAstEditor() { 510 super(); 511 scroll.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); 512 textArea.addCaretListener(this); 513 } 514 515 public void caretUpdate(CaretEvent arg0) { 516 currentTestCase.getOutput().setScript(getText()); 517 listCases.updateUI(); 518 } 519 520 public void setText(String text) { 521 this.textArea.setText(text); 522 } 523 524 public String getText() { 525 return this.textArea.getText(); 526 } 527 528 public JScrollPane getView() { 529 return this.scroll; 530 } 531 } 532 533 534 public class OutputStdEditor extends OutputAstEditor {} 535 public class OutputReturnEditor extends OutputAstEditor {} 536 537 /* EVENT HANDLERS */ 538 539 private class TestCaseListSelectionListener implements ListSelectionListener { 540 541 public void valueChanged(ListSelectionEvent e) { 542 543 if(e.getValueIsAdjusting()) return; 544 final JList list = (JList) e.getSource(); 545 final TestCase value = (TestCase) list.getSelectedValue(); 546 if(value != null) OnTestCaseSelected(value); 547 548 } 549 550 } 551 552 /* ACTIONS */ 553 554 private class AddTestCaseAction extends AbstractAction { 555 public AddTestCaseAction() { 556 super("Add", ImageFactory.getSingleton().ADD); 557 putValue(SHORT_DESCRIPTION, "Add a gUnit test case."); 558 } 559 public void actionPerformed(ActionEvent e) { 560 OnAddTestCase(); 561 } 562 } 563 564 private class RemoveTestCaseAction extends AbstractAction { 565 public RemoveTestCaseAction() { 566 super("Remove", ImageFactory.getSingleton().DELETE); 567 putValue(SHORT_DESCRIPTION, "Remove a gUnit test case."); 568 } 569 public void actionPerformed(ActionEvent e) { 570 OnRemoveTestCase(); 571 } 572 } 573 574 /* CELL RENDERERS */ 575 576 private static TestCaseListRenderer listRenderer 577 = new TestCaseListRenderer(); 578 579 private static class TestCaseListRenderer implements ListCellRenderer { 580 581 private static Font IN_FONT = new Font("mono", Font.PLAIN, 12); 582 private static Font OUT_FONT = new Font("default", Font.BOLD, 12); 583 584 public static String clamp(String text, int len) { 585 if(text.length() > len) { 586 return text.substring(0, len - 3).concat("..."); 587 } else { 588 return text; 589 } 590 } 591 592 public static String clampAtNewLine(String text) { 593 int pos = text.indexOf('\n'); 594 if(pos >= 0) { 595 return text.substring(0, pos).concat("..."); 596 } else { 597 return text; 598 } 599 } 600 601 public Component getListCellRendererComponent( 602 JList list, Object value, int index, 603 boolean isSelected, boolean hasFocus) { 604 605 final JPanel pane = new JPanel(); 606 607 if (value instanceof TestCase) { 608 final TestCase item = (TestCase) value; 609 610 // create components 611 final JLabel labIn = new JLabel( 612 clamp(clampAtNewLine(item.getInput().getScript()), 18)); 613 final JLabel labOut = new JLabel( 614 clamp(clampAtNewLine(item.getOutput().getScript()), 18)); 615 labOut.setFont(OUT_FONT); 616 labIn.setFont(IN_FONT); 617 618 labIn.setIcon(item.getInput() instanceof TestCaseInputFile ? 619 ImageFactory.getSingleton().FILE16 : 620 ImageFactory.getSingleton().EDIT16); 621 622 pane.setBorder(BorderFactory.createEtchedBorder()); 623 pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); 624 pane.add(labIn); 625 pane.add(labOut); 626 pane.setBackground(isSelected ? Color.LIGHT_GRAY : Color.WHITE); 627 } 628 629 return pane; 630 } 631 } 632 633 } 634