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.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.setLinePositionList(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.setLinePositionList(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}