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