001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.gui;
021
022import java.awt.BorderLayout;
023import java.awt.FlowLayout;
024import java.awt.GridLayout;
025import java.awt.event.ActionEvent;
026import java.awt.event.KeyEvent;
027import java.io.File;
028
029import javax.swing.AbstractAction;
030import javax.swing.BorderFactory;
031import javax.swing.JButton;
032import javax.swing.JComboBox;
033import javax.swing.JFileChooser;
034import javax.swing.JFrame;
035import javax.swing.JLabel;
036import javax.swing.JOptionPane;
037import javax.swing.JPanel;
038import javax.swing.JScrollPane;
039import javax.swing.JSplitPane;
040import javax.swing.JTextArea;
041import javax.swing.SwingConstants;
042import javax.swing.border.Border;
043import javax.swing.filechooser.FileFilter;
044
045import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
046import com.puppycrawl.tools.checkstyle.gui.MainFrameModel.ParseMode;
047
048/**
049 * Displays information about a parse tree.
050 * The user can change the file that is parsed and displayed
051 * using a JFileChooser.
052 *
053 * @noinspection MagicNumber
054 */
055public class MainFrame extends JFrame {
056
057    private static final long serialVersionUID = 7970053543351871890L;
058
059    /** Checkstyle frame model. */
060    private final transient MainFrameModel model = new MainFrameModel();
061    /** Reload action. */
062    private final ReloadAction reloadAction = new ReloadAction();
063    /** Code text area. */
064    private JTextArea textArea;
065    /** Xpath text area. */
066    private JTextArea xpathTextArea;
067    /** Tree table. */
068    private TreeTable treeTable;
069
070    /** Create a new MainFrame. */
071    public MainFrame() {
072        createContent();
073    }
074
075    /** Create content of this MainFrame. */
076    private void createContent() {
077        setLayout(new BorderLayout());
078
079        textArea = new JTextArea(20, 15);
080        textArea.setEditable(false);
081        final JScrollPane textAreaScrollPane = new JScrollPane(textArea);
082        final JPanel textAreaPanel = new JPanel();
083        textAreaPanel.setLayout(new BorderLayout());
084        textAreaPanel.add(textAreaScrollPane);
085        textAreaPanel.add(createButtonsPanel(), BorderLayout.PAGE_END);
086
087        treeTable = new TreeTable(model.getParseTreeTableModel());
088        treeTable.setEditor(textArea);
089        treeTable.setLinePositionMap(model.getLinesToPosition());
090        final JScrollPane treeTableScrollPane = new JScrollPane(treeTable);
091
092        final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
093            treeTableScrollPane, textAreaPanel);
094
095        add(splitPane, BorderLayout.CENTER);
096        splitPane.setResizeWeight(0.7);
097
098        xpathTextArea = new JTextArea("Xpath", 7, 0);
099        xpathTextArea.setVisible(false);
100        final JPanel xpathAreaPanel = new JPanel();
101        xpathAreaPanel.setLayout(new BorderLayout());
102        xpathAreaPanel.add(xpathTextArea);
103        xpathAreaPanel.add(createXpathButtonsPanel(), BorderLayout.PAGE_END);
104
105        treeTable.setXpathEditor(xpathTextArea);
106
107        final Border title = BorderFactory.createTitledBorder("Xpath Query");
108        xpathAreaPanel.setBorder(title);
109
110        add(xpathAreaPanel, BorderLayout.PAGE_END);
111
112        pack();
113    }
114
115    /**
116     * Create buttons panel.
117     *
118     * @return buttons panel.
119     */
120    private JPanel createButtonsPanel() {
121        final JButton openFileButton = new JButton(new FileSelectionAction());
122        openFileButton.setMnemonic(KeyEvent.VK_S);
123        openFileButton.setText("Open File");
124
125        reloadAction.setEnabled(false);
126        final JButton reloadFileButton = new JButton(reloadAction);
127        reloadFileButton.setMnemonic(KeyEvent.VK_R);
128        reloadFileButton.setText("Reload File");
129
130        final JComboBox<ParseMode> modesCombobox = new JComboBox<>(ParseMode.values());
131        modesCombobox.setSelectedIndex(0);
132        modesCombobox.addActionListener(event -> {
133            model.setParseMode((ParseMode) modesCombobox.getSelectedItem());
134            reloadAction.actionPerformed(null);
135        });
136
137        final JLabel modesLabel = new JLabel("Modes:", SwingConstants.RIGHT);
138        final int leftIndentation = 10;
139        modesLabel.setBorder(BorderFactory.createEmptyBorder(0, leftIndentation, 0, 0));
140
141        final JPanel buttonPanel = new JPanel();
142        buttonPanel.setLayout(new GridLayout(1, 2));
143        buttonPanel.add(openFileButton);
144        buttonPanel.add(reloadFileButton);
145
146        final JPanel modesPanel = new JPanel();
147        modesPanel.add(modesLabel);
148        modesPanel.add(modesCombobox);
149
150        final JPanel mainPanel = new JPanel();
151        mainPanel.setLayout(new BorderLayout());
152        mainPanel.add(buttonPanel);
153        mainPanel.add(modesPanel, BorderLayout.LINE_END);
154
155        return mainPanel;
156    }
157
158    /**
159     * Create xpath buttons panel.
160     *
161     * @return xpath buttons panel.
162     */
163    private JPanel createXpathButtonsPanel() {
164        final JButton expandButton = new JButton(new ExpandCollapseAction());
165        expandButton.setText("Expand/Collapse");
166
167        final JButton findNodeButton = new JButton(new FindNodeByXpathAction());
168        findNodeButton.setText("Find node by Xpath");
169
170        final JPanel xpathButtonsPanel = new JPanel();
171        xpathButtonsPanel.setLayout(new FlowLayout());
172        xpathButtonsPanel.add(expandButton);
173        xpathButtonsPanel.add(findNodeButton);
174
175        final JPanel mainPanel = new JPanel();
176        mainPanel.setLayout(new BorderLayout());
177        mainPanel.add(xpathButtonsPanel, BorderLayout.LINE_START);
178
179        return mainPanel;
180    }
181
182    /**
183     * Open file and load it.
184     *
185     * @param sourceFile the file to open.
186     */
187    public void openFile(File sourceFile) {
188        try {
189            model.openFile(sourceFile);
190            setTitle(model.getTitle());
191            reloadAction.setEnabled(model.isReloadActionEnabled());
192            textArea.setText(model.getText());
193            treeTable.setLinePositionMap(model.getLinesToPosition());
194        }
195        catch (final CheckstyleException ex) {
196            JOptionPane.showMessageDialog(this, ex.getMessage());
197        }
198    }
199
200    /**
201     * Handler for file selection action events.
202     */
203    private class FileSelectionAction extends AbstractAction {
204
205        private static final long serialVersionUID = 1762396148873280589L;
206
207        @Override
208        public void actionPerformed(ActionEvent event) {
209            final JFileChooser fileChooser = new JFileChooser(model.getLastDirectory());
210            final FileFilter filter = new JavaFileFilter();
211            fileChooser.setFileFilter(filter);
212
213            final int returnCode = fileChooser.showOpenDialog(MainFrame.this);
214            if (returnCode == JFileChooser.APPROVE_OPTION) {
215                final File file = fileChooser.getSelectedFile();
216                openFile(file);
217            }
218        }
219
220    }
221
222    /**
223     * Handler for reload action events.
224     */
225    private class ReloadAction extends AbstractAction {
226
227        private static final long serialVersionUID = -890320994114628011L;
228
229        @Override
230        public void actionPerformed(ActionEvent event) {
231            openFile(model.getCurrentFile());
232        }
233
234    }
235
236    /**
237     * Handler for Expand and Collapse events.
238     */
239    private class ExpandCollapseAction extends AbstractAction {
240
241        private static final long serialVersionUID = -890320994114628011L;
242
243        @Override
244        public void actionPerformed(ActionEvent event) {
245            xpathTextArea.setVisible(!xpathTextArea.isVisible());
246        }
247
248    }
249
250    /**
251     * Handler for Find Node by Xpath Event.
252     */
253    private class FindNodeByXpathAction extends AbstractAction {
254
255        private static final long serialVersionUID = -890320994114628011L;
256
257        @Override
258        public void actionPerformed(ActionEvent event) {
259            treeTable.selectNodeByXpath();
260        }
261
262    }
263
264    /**
265     * Filter for Java files.
266     */
267    private static class JavaFileFilter extends FileFilter {
268
269        @Override
270        public boolean accept(File file) {
271            return MainFrameModel.shouldAcceptFile(file);
272        }
273
274        @Override
275        public String getDescription() {
276            return "Java Source File";
277        }
278
279    }
280
281}