001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2002 Oliver Burn 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.Component; 024import java.awt.GridLayout; 025import java.awt.event.ActionEvent; 026import java.awt.event.KeyEvent; 027import java.io.File; 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.List; 031import java.util.TooManyListenersException; 032import javax.swing.AbstractAction; 033import javax.swing.Action; 034import javax.swing.JButton; 035import javax.swing.JFileChooser; 036import javax.swing.JOptionPane; 037import javax.swing.JPanel; 038import javax.swing.JScrollPane; 039import javax.swing.JTextArea; 040import javax.swing.SwingUtilities; 041import javax.swing.filechooser.FileFilter; 042import antlr.ANTLRException; 043import com.puppycrawl.tools.checkstyle.TreeWalker; 044import com.puppycrawl.tools.checkstyle.api.DetailAST; 045import com.puppycrawl.tools.checkstyle.api.FileContents; 046import com.puppycrawl.tools.checkstyle.api.FileText; 047 048/** 049 * Displays information about a parse tree. 050 * The user can change the file that is parsed and displayed 051 * through a JFileChooser. 052 * 053 * @author Lars K?hne 054 */ 055public class ParseTreeInfoPanel extends JPanel 056{ 057 /** For Serialisation that will never happen. */ 058 private static final long serialVersionUID = -4243405131202059043L; 059 private final JTreeTable mTreeTable; 060 private final ParseTreeModel mParseTreeModel; 061 private final JTextArea mJTextArea; 062 private File mLastDirectory = null; 063 private File mCurrentFile = null; 064 private final Action reloadAction; 065 private final List<Integer> lines2position = new ArrayList<Integer>(); 066 067 private static class JavaFileFilter extends FileFilter 068 { 069 @Override 070 public boolean accept(File f) 071 { 072 if (f == null) { 073 return false; 074 } 075 return f.isDirectory() || f.getName().endsWith(".java"); 076 } 077 078 @Override 079 public String getDescription() 080 { 081 return "Java Source Code"; 082 } 083 } 084 085 private class FileSelectionAction extends AbstractAction 086 { 087 /** 088 * 089 */ 090 private static final long serialVersionUID = -1926935338069418119L; 091 092 public FileSelectionAction() 093 { 094 super("Select Java File"); 095 putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S); 096 } 097 098 public void actionPerformed(ActionEvent e) 099 { 100 final JFileChooser fc = new JFileChooser( mLastDirectory ); 101 final FileFilter filter = new JavaFileFilter(); 102 fc.setFileFilter(filter); 103 final Component parent = 104 SwingUtilities.getRoot(ParseTreeInfoPanel.this); 105 fc.showDialog(parent, "Open"); 106 final File file = fc.getSelectedFile(); 107 openFile(file, parent); 108 109 } 110 } 111 112 private class ReloadAction extends AbstractAction 113 { 114 /** 115 * 116 */ 117 private static final long serialVersionUID = -1021880396046355863L; 118 119 public ReloadAction() 120 { 121 super("Reload Java File"); 122 putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R); 123 } 124 125 public void actionPerformed(ActionEvent e) 126 { 127 final Component parent = 128 SwingUtilities.getRoot(ParseTreeInfoPanel.this); 129 openFile(mCurrentFile, parent); 130 } 131 } 132 133 134 private class FileDropListener implements FileDrop.Listener 135 { 136 private final JScrollPane mSp; 137 138 public void filesDropped(File[] files) 139 { 140 if ((files != null) && (files.length > 0)) 141 { 142 final File file = files[0]; 143 openFile(file, mSp); 144 } 145 } 146 147 public FileDropListener(JScrollPane aSp) 148 { 149 mSp = aSp; 150 } 151 } 152 153 154 public void openFile(File aFile, final Component aParent) 155 { 156 if (aFile != null) { 157 try { 158 Main.frame.setTitle("Checkstyle : " + aFile.getName()); 159 final FileText text = new FileText(aFile.getAbsoluteFile(), 160 getEncoding()); 161 final DetailAST parseTree = parseFile(text); 162 mParseTreeModel.setParseTree(parseTree); 163 mCurrentFile = aFile; 164 mLastDirectory = aFile.getParentFile(); 165 reloadAction.setEnabled(true); 166 167 final String[] sourceLines = text.toLinesArray(); 168 169 // clear for each new file 170 getLines2position().clear(); 171 // starts line counting at 1 172 getLines2position().add(0); 173 // insert the contents of the file to the text area 174 for (String element : sourceLines) 175 { 176 getLines2position().add(mJTextArea.getText().length()); 177 mJTextArea.append(element + "\n"); 178 } 179 180 //clean the text area before inserting the lines of the new file 181 if (mJTextArea.getText().length() != 0) { 182 mJTextArea.replaceRange("", 0, mJTextArea.getText() 183 .length()); 184 } 185 186 // insert the contents of the file to the text area 187 for (final String element : sourceLines) { 188 mJTextArea.append(element + "\n"); 189 } 190 191 // move back to the top of the file 192 mJTextArea.moveCaretPosition(0); 193 } 194 catch (final IOException ex) { 195 showErrorDialog( 196 aParent, 197 "Could not open " + aFile + ": " + ex.getMessage()); 198 } 199 catch (final ANTLRException ex) { 200 showErrorDialog( 201 aParent, 202 "Could not parse " + aFile + ": " + ex.getMessage()); 203 } 204 } 205 } 206 207 /** 208 * Parses a file and returns the parse tree. 209 * @param aFileName the file to parse 210 * @return the root node of the parse tree 211 * @throws IOException if the file cannot be opened 212 * @throws ANTLRException if the file is not a Java source 213 * @deprecated Use {@link #parseFile(FileText)} instead 214 */ 215 @Deprecated 216 public static DetailAST parseFile(String aFileName) 217 throws IOException, ANTLRException 218 { 219 return parseFile(new FileText(new File(aFileName), getEncoding())); 220 } 221 222 /** 223 * Parses a file and returns the parse tree. 224 * @param aText the file to parse 225 * @return the root node of the parse tree 226 * @throws ANTLRException if the file is not a Java source 227 */ 228 public static DetailAST parseFile(FileText aText) 229 throws ANTLRException 230 { 231 final FileContents contents = new FileContents(aText); 232 return TreeWalker.parse(contents); 233 } 234 235 /** 236 * Returns the configured file encoding. 237 * This can be set using the {@code file.encoding} system property. 238 * It defaults to UTF-8. 239 * @return the configured file encoding 240 */ 241 private static String getEncoding() 242 { 243 return System.getProperty("file.encoding", "UTF-8"); 244 } 245 246 /** 247 * Create a new ParseTreeInfoPanel instance. 248 */ 249 public ParseTreeInfoPanel() 250 { 251 setLayout(new BorderLayout()); 252 253 final DetailAST treeRoot = null; 254 mParseTreeModel = new ParseTreeModel(treeRoot); 255 mTreeTable = new JTreeTable(mParseTreeModel); 256 final JScrollPane sp = new JScrollPane(mTreeTable); 257 this.add(sp, BorderLayout.NORTH); 258 259 final JButton fileSelectionButton = 260 new JButton(new FileSelectionAction()); 261 262 reloadAction = new ReloadAction(); 263 reloadAction.setEnabled(false); 264 final JButton reloadButton = new JButton(reloadAction); 265 266 mJTextArea = new JTextArea(20, 15); 267 mJTextArea.setEditable(false); 268 mTreeTable.setEditor(mJTextArea); 269 mTreeTable.setLinePositionMap(lines2position); 270 271 final JScrollPane sp2 = new JScrollPane(mJTextArea); 272 this.add(sp2, BorderLayout.CENTER); 273 274 final JPanel p = new JPanel(new GridLayout(1,2)); 275 this.add(p, BorderLayout.SOUTH); 276 p.add(fileSelectionButton); 277 p.add(reloadButton); 278 279 try { 280 // TODO: creating an object for the side effect of the constructor 281 // and then ignoring the object looks strange. 282 new FileDrop(sp, new FileDropListener(sp)); 283 } 284 catch (final TooManyListenersException ex) 285 { 286 showErrorDialog(null, "Cannot initialize Drag and Drop support"); 287 } 288 289 } 290 291 private void showErrorDialog(final Component parent, final String msg) 292 { 293 final Runnable showError = new Runnable() 294 { 295 public void run() 296 { 297 JOptionPane.showMessageDialog(parent, msg); 298 } 299 }; 300 SwingUtilities.invokeLater(showError); 301 } 302 303 public List<Integer> getLines2position() 304 { 305 return lines2position; 306 } 307} 308