001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2022 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.Toolkit;
026import java.awt.event.ActionEvent;
027import java.awt.event.KeyEvent;
028import java.io.File;
029
030import javax.swing.AbstractAction;
031import javax.swing.BorderFactory;
032import javax.swing.JButton;
033import javax.swing.JComboBox;
034import javax.swing.JFileChooser;
035import javax.swing.JFrame;
036import javax.swing.JLabel;
037import javax.swing.JOptionPane;
038import javax.swing.JPanel;
039import javax.swing.JScrollPane;
040import javax.swing.JSplitPane;
041import javax.swing.JTextArea;
042import javax.swing.SwingConstants;
043import javax.swing.border.Border;
044import javax.swing.filechooser.FileFilter;
045
046import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
047import com.puppycrawl.tools.checkstyle.gui.MainFrameModel.ParseMode;
048
049/**
050 * Displays information about a parse tree.
051 * The user can change the file that is parsed and displayed
052 * using a JFileChooser.
053 *
054 * @noinspection MagicNumber
055 */
056public class MainFrame extends JFrame {
057
058    /** A unique serial version identifier. */
059    private static final long serialVersionUID = 7970053543351871890L;
060
061    /** The icon to show in the OS task panel. */
062    private static final String ICON = "icon.png";
063
064    /** Checkstyle frame model. */
065    private final transient MainFrameModel model = new MainFrameModel();
066    /** Reload action. */
067    private final ReloadAction reloadAction = new ReloadAction();
068    /** Code text area. */
069    private JTextArea textArea;
070    /** Xpath text area. */
071    private JTextArea xpathTextArea;
072    /** Tree table. */
073    private TreeTable treeTable;
074
075    /** Create a new MainFrame. */
076    public MainFrame() {
077        createContent();
078    }
079
080    /** Create content of this MainFrame. */
081    private void createContent() {
082        setLayout(new BorderLayout());
083        setIconImage(Toolkit.getDefaultToolkit().getImage(MainFrame.class.getResource(ICON)));
084
085        textArea = new JTextArea(20, 15);
086        textArea.setEditable(false);
087        final JScrollPane textAreaScrollPane = new JScrollPane(textArea);
088        final JPanel textAreaPanel = new JPanel();
089        textAreaPanel.setLayout(new BorderLayout());
090        textAreaPanel.add(textAreaScrollPane);
091        textAreaPanel.add(createButtonsPanel(), BorderLayout.PAGE_END);
092
093        treeTable = new TreeTable(model.getParseTreeTableModel());
094        treeTable.setEditor(textArea);
095        treeTable.setLinePositionList(model.getLinesToPosition());
096        final JScrollPane treeTableScrollPane = new JScrollPane(treeTable);
097
098        final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
099            treeTableScrollPane, textAreaPanel);
100
101        add(splitPane, BorderLayout.CENTER);
102        splitPane.setResizeWeight(0.7);
103
104        xpathTextArea = new JTextArea("Xpath", 7, 0);
105        xpathTextArea.setVisible(false);
106        final JPanel xpathAreaPanel = new JPanel();
107        xpathAreaPanel.setLayout(new BorderLayout());
108        xpathAreaPanel.add(xpathTextArea);
109        xpathAreaPanel.add(createXpathButtonsPanel(), BorderLayout.PAGE_END);
110
111        treeTable.setXpathEditor(xpathTextArea);
112
113        final Border title = BorderFactory.createTitledBorder("Xpath Query");
114        xpathAreaPanel.setBorder(title);
115
116        add(xpathAreaPanel, BorderLayout.PAGE_END);
117    }
118
119    /**
120     * Create buttons panel.
121     *
122     * @return buttons panel.
123     */
124    private JPanel createButtonsPanel() {
125        final JButton openFileButton = new JButton(new FileSelectionAction());
126        openFileButton.setMnemonic(KeyEvent.VK_O);
127        openFileButton.setText("Open File");
128
129        reloadAction.setEnabled(false);
130        final JButton reloadFileButton = new JButton(reloadAction);
131        reloadFileButton.setMnemonic(KeyEvent.VK_R);
132        reloadFileButton.setText("Reload File");
133
134        final JComboBox<ParseMode> modesCombobox = new JComboBox<>(ParseMode.values());
135        modesCombobox.setSelectedIndex(0);
136        modesCombobox.addActionListener(event -> {
137            model.setParseMode((ParseMode) modesCombobox.getSelectedItem());
138            reloadAction.actionPerformed(null);
139        });
140
141        final JLabel modesLabel = new JLabel("Modes:", SwingConstants.RIGHT);
142        final int leftIndentation = 10;
143        modesLabel.setBorder(BorderFactory.createEmptyBorder(0, leftIndentation, 0, 0));
144
145        final JPanel buttonPanel = new JPanel();
146        buttonPanel.setLayout(new GridLayout(1, 2));
147        buttonPanel.add(openFileButton);
148        buttonPanel.add(reloadFileButton);
149
150        final JPanel modesPanel = new JPanel();
151        modesPanel.add(modesLabel);
152        modesPanel.add(modesCombobox);
153
154        final JPanel mainPanel = new JPanel();
155        mainPanel.setLayout(new BorderLayout());
156        mainPanel.add(buttonPanel);
157        mainPanel.add(modesPanel, BorderLayout.LINE_END);
158
159        return mainPanel;
160    }
161
162    /**
163     * Create xpath buttons panel.
164     *
165     * @return xpath buttons panel.
166     */
167    private JPanel createXpathButtonsPanel() {
168        final JButton expandButton = new JButton(new ExpandCollapseAction());
169        expandButton.setText("Expand/Collapse");
170
171        final JButton findNodeButton = new JButton(new FindNodeByXpathAction());
172        findNodeButton.setText("Find node by Xpath");
173
174        final JPanel xpathButtonsPanel = new JPanel();
175        xpathButtonsPanel.setLayout(new FlowLayout());
176        xpathButtonsPanel.add(expandButton);
177        xpathButtonsPanel.add(findNodeButton);
178
179        final JPanel mainPanel = new JPanel();
180        mainPanel.setLayout(new BorderLayout());
181        mainPanel.add(xpathButtonsPanel, BorderLayout.LINE_START);
182
183        return mainPanel;
184    }
185
186    /**
187     * Open file and load it.
188     *
189     * @param sourceFile the file to open.
190     */
191    public void openFile(File sourceFile) {
192        try {
193            model.openFile(sourceFile);
194            setTitle(model.getTitle());
195            reloadAction.setEnabled(model.isReloadActionEnabled());
196            textArea.setText(model.getText());
197            treeTable.setLinePositionList(model.getLinesToPosition());
198        }
199        catch (final CheckstyleException ex) {
200            JOptionPane.showMessageDialog(this, ex.getMessage());
201        }
202    }
203
204    /**
205     * Handler for file selection action events.
206     */
207    private class FileSelectionAction extends AbstractAction {
208
209        /** A unique serial version identifier. */
210        private static final long serialVersionUID = 1762396148873280589L;
211
212        @Override
213        public void actionPerformed(ActionEvent event) {
214            final JFileChooser fileChooser = new JFileChooser(model.getLastDirectory());
215            final FileFilter filter = new JavaFileFilter();
216            fileChooser.setFileFilter(filter);
217
218            final int returnCode = fileChooser.showOpenDialog(MainFrame.this);
219            if (returnCode == JFileChooser.APPROVE_OPTION) {
220                final File file = fileChooser.getSelectedFile();
221                openFile(file);
222            }
223        }
224
225    }
226
227    /**
228     * Handler for reload action events.
229     */
230    private class ReloadAction extends AbstractAction {
231
232        /** A unique serial version identifier. */
233        private static final long serialVersionUID = -890320994114628011L;
234
235        @Override
236        public void actionPerformed(ActionEvent event) {
237            openFile(model.getCurrentFile());
238        }
239
240    }
241
242    /**
243     * Handler for Expand and Collapse events.
244     */
245    private class ExpandCollapseAction extends AbstractAction {
246
247        /** A unique serial version identifier. */
248        private static final long serialVersionUID = -890320994114628011L;
249
250        @Override
251        public void actionPerformed(ActionEvent event) {
252            xpathTextArea.setVisible(!xpathTextArea.isVisible());
253        }
254
255    }
256
257    /**
258     * Handler for Find Node by Xpath Event.
259     */
260    private class FindNodeByXpathAction extends AbstractAction {
261
262        /** A unique serial version identifier. */
263        private static final long serialVersionUID = -890320994114628011L;
264
265        @Override
266        public void actionPerformed(ActionEvent event) {
267            treeTable.selectNodeByXpath();
268        }
269
270    }
271
272    /**
273     * Filter for Java files.
274     */
275    private static class JavaFileFilter extends FileFilter {
276
277        @Override
278        public boolean accept(File file) {
279            return MainFrameModel.shouldAcceptFile(file);
280        }
281
282        @Override
283        public String getDescription() {
284            return "Java Source File";
285        }
286
287    }
288
289}