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