Home | History | Annotate | Download | only in swingui
      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