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}