View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.gui;
21  
22  import java.awt.BorderLayout;
23  import java.awt.FlowLayout;
24  import java.awt.GridLayout;
25  import java.awt.Toolkit;
26  import java.awt.event.ActionEvent;
27  import java.awt.event.KeyEvent;
28  import java.io.File;
29  import java.io.Serial;
30  
31  import javax.swing.AbstractAction;
32  import javax.swing.BorderFactory;
33  import javax.swing.JButton;
34  import javax.swing.JComboBox;
35  import javax.swing.JFileChooser;
36  import javax.swing.JFrame;
37  import javax.swing.JLabel;
38  import javax.swing.JOptionPane;
39  import javax.swing.JPanel;
40  import javax.swing.JScrollPane;
41  import javax.swing.JSplitPane;
42  import javax.swing.JTextArea;
43  import javax.swing.SwingConstants;
44  import javax.swing.border.Border;
45  import javax.swing.filechooser.FileFilter;
46  
47  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
48  import com.puppycrawl.tools.checkstyle.gui.MainFrameModel.ParseMode;
49  
50  /**
51   * Displays information about a parse tree.
52   * The user can change the file that is parsed and displayed
53   * using a JFileChooser.
54   *
55   * @noinspection MagicNumber
56   * @noinspectionreason MagicNumber - "magic numbers" are required to set GUI elements
57   */
58  public class MainFrame extends JFrame {
59  
60      /** A unique serial version identifier. */
61      @Serial
62      private static final long serialVersionUID = 7970053543351871890L;
63  
64      /** The icon to show in the OS task panel. */
65      private static final String ICON = "icon.png";
66  
67      /** Checkstyle frame model. */
68      private final transient MainFrameModel model = new MainFrameModel();
69      /** Reload action. */
70      private final ReloadAction reloadAction = new ReloadAction();
71      /** Code text area. */
72      private JTextArea textArea;
73      /** Xpath text area. */
74      private JTextArea xpathTextArea;
75      /** Tree table. */
76      private TreeTable treeTable;
77  
78      /** Create a new MainFrame. */
79      public MainFrame() {
80          createContent();
81      }
82  
83      /** Create content of this MainFrame. */
84      private void createContent() {
85          setLayout(new BorderLayout());
86          setIconImage(Toolkit.getDefaultToolkit().getImage(MainFrame.class.getResource(ICON)));
87  
88          textArea = new JTextArea(20, 15);
89          textArea.setEditable(false);
90          final JScrollPane textAreaScrollPane = new JScrollPane(textArea);
91          final JPanel textAreaPanel = new JPanel();
92          textAreaPanel.setLayout(new BorderLayout());
93          textAreaPanel.add(textAreaScrollPane);
94          textAreaPanel.add(createButtonsPanel(), BorderLayout.PAGE_END);
95  
96          treeTable = new TreeTable(model.getParseTreeTableModel());
97          treeTable.setEditor(textArea);
98          treeTable.setLinePositionList(model.getLinesToPosition());
99          final JScrollPane treeTableScrollPane = new JScrollPane(treeTable);
100 
101         final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
102             treeTableScrollPane, textAreaPanel);
103 
104         add(splitPane, BorderLayout.CENTER);
105         splitPane.setResizeWeight(0.7);
106 
107         xpathTextArea = new JTextArea("Xpath", 7, 0);
108         xpathTextArea.setName("xpathTextArea");
109         xpathTextArea.setVisible(false);
110         final JPanel xpathAreaPanel = new JPanel();
111         xpathAreaPanel.setLayout(new BorderLayout());
112         xpathAreaPanel.add(xpathTextArea);
113         xpathAreaPanel.add(createXpathButtonsPanel(), BorderLayout.PAGE_END);
114 
115         treeTable.setXpathEditor(xpathTextArea);
116 
117         final Border title = BorderFactory.createTitledBorder("Xpath Query");
118         xpathAreaPanel.setBorder(title);
119 
120         add(xpathAreaPanel, BorderLayout.PAGE_END);
121     }
122 
123     /**
124      * Create buttons panel.
125      *
126      * @return buttons panel.
127      */
128     private JPanel createButtonsPanel() {
129         final JButton openFileButton = new JButton(new FileSelectionAction());
130         openFileButton.setName("openFileButton");
131         openFileButton.setMnemonic(KeyEvent.VK_O);
132         openFileButton.setText("Open File");
133 
134         reloadAction.setEnabled(false);
135         final JButton reloadFileButton = new JButton(reloadAction);
136         reloadFileButton.setMnemonic(KeyEvent.VK_R);
137         reloadFileButton.setText("Reload File");
138 
139         final JComboBox<ParseMode> modesCombobox = new JComboBox<>(ParseMode.values());
140         modesCombobox.setName("modesCombobox");
141         modesCombobox.setSelectedIndex(0);
142         modesCombobox.addActionListener(event -> {
143             model.setParseMode((ParseMode) modesCombobox.getSelectedItem());
144             reloadAction.actionPerformed(null);
145         });
146 
147         final JLabel modesLabel = new JLabel("Modes:", SwingConstants.RIGHT);
148         final int leftIndentation = 10;
149         modesLabel.setBorder(BorderFactory.createEmptyBorder(0, leftIndentation, 0, 0));
150         modesLabel.setDisplayedMnemonic(KeyEvent.VK_M);
151         modesLabel.setLabelFor(modesCombobox);
152 
153         final JPanel buttonPanel = new JPanel();
154         buttonPanel.setLayout(new GridLayout(1, 2));
155         buttonPanel.add(openFileButton);
156         buttonPanel.add(reloadFileButton);
157 
158         final JPanel modesPanel = new JPanel();
159         modesPanel.add(modesLabel);
160         modesPanel.add(modesCombobox);
161 
162         final JPanel mainPanel = new JPanel();
163         mainPanel.setLayout(new BorderLayout());
164         mainPanel.add(buttonPanel);
165         mainPanel.add(modesPanel, BorderLayout.LINE_END);
166 
167         return mainPanel;
168     }
169 
170     /**
171      * Create xpath buttons panel.
172      *
173      * @return xpath buttons panel.
174      */
175     private JPanel createXpathButtonsPanel() {
176         final JButton expandButton = new JButton(new ExpandCollapseAction());
177         expandButton.setName("expandButton");
178         expandButton.setText("Expand/Collapse");
179 
180         final JButton findNodeButton = new JButton(new FindNodeByXpathAction());
181         findNodeButton.setName("findNodeButton");
182         findNodeButton.setText("Find node by Xpath");
183 
184         final JPanel xpathButtonsPanel = new JPanel();
185         xpathButtonsPanel.setLayout(new FlowLayout());
186         xpathButtonsPanel.add(expandButton);
187         xpathButtonsPanel.add(findNodeButton);
188 
189         final JPanel mainPanel = new JPanel();
190         mainPanel.setLayout(new BorderLayout());
191         mainPanel.add(xpathButtonsPanel, BorderLayout.LINE_START);
192 
193         return mainPanel;
194     }
195 
196     /**
197      * Open file and load it.
198      *
199      * @param sourceFile the file to open.
200      */
201     public void openFile(File sourceFile) {
202         try {
203             model.openFile(sourceFile);
204             setTitle(model.getTitle());
205             reloadAction.setEnabled(model.isReloadActionEnabled());
206             textArea.setText(model.getText());
207             treeTable.setLinePositionList(model.getLinesToPosition());
208         }
209         catch (final CheckstyleException exc) {
210             JOptionPane.showMessageDialog(this, exc.getMessage());
211         }
212     }
213 
214     /**
215      * Handler for file selection action events.
216      */
217     private final class FileSelectionAction extends AbstractAction {
218 
219         /** A unique serial version identifier. */
220         @Serial
221         private static final long serialVersionUID = 1762396148873280589L;
222 
223         @Override
224         public void actionPerformed(ActionEvent event) {
225             final JFileChooser fileChooser = new JFileChooser(model.getLastDirectory());
226             final FileFilter filter = new JavaFileFilter();
227             fileChooser.setFileFilter(filter);
228 
229             final int returnCode = fileChooser.showOpenDialog(MainFrame.this);
230             if (returnCode == JFileChooser.APPROVE_OPTION) {
231                 final File file = fileChooser.getSelectedFile();
232                 openFile(file);
233             }
234         }
235 
236     }
237 
238     /**
239      * Handler for reload action events.
240      */
241     private final class ReloadAction extends AbstractAction {
242 
243         /** A unique serial version identifier. */
244         @Serial
245         private static final long serialVersionUID = -890320994114628011L;
246 
247         @Override
248         public void actionPerformed(ActionEvent event) {
249             openFile(model.getCurrentFile());
250         }
251 
252     }
253 
254     /**
255      * Handler for Expand and Collapse events.
256      */
257     private final class ExpandCollapseAction extends AbstractAction {
258 
259         /** A unique serial version identifier. */
260         @Serial
261         private static final long serialVersionUID = -890320994114628011L;
262 
263         @Override
264         public void actionPerformed(ActionEvent event) {
265             xpathTextArea.setVisible(!xpathTextArea.isVisible());
266         }
267 
268     }
269 
270     /**
271      * Handler for Find Node by Xpath Event.
272      */
273     private final class FindNodeByXpathAction extends AbstractAction {
274 
275         /** A unique serial version identifier. */
276         @Serial
277         private static final long serialVersionUID = -890320994114628011L;
278 
279         @Override
280         public void actionPerformed(ActionEvent event) {
281             treeTable.selectNodeByXpath();
282         }
283 
284     }
285 
286     /**
287      * Filter for Java files.
288      */
289     private static final class JavaFileFilter extends FileFilter {
290 
291         @Override
292         public boolean accept(File file) {
293             return MainFrameModel.shouldAcceptFile(file);
294         }
295 
296         @Override
297         public String getDescription() {
298             return "Java Source File";
299         }
300 
301     }
302 
303 }