View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2002  Oliver Burn
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.Component;
24  import java.awt.GridLayout;
25  import java.awt.event.ActionEvent;
26  import java.awt.event.KeyEvent;
27  import java.io.File;
28  import java.io.IOException;
29  import java.util.ArrayList;
30  import java.util.List;
31  import java.util.TooManyListenersException;
32  
33  import javax.swing.AbstractAction;
34  import javax.swing.Action;
35  import javax.swing.JButton;
36  import javax.swing.JFileChooser;
37  import javax.swing.JOptionPane;
38  import javax.swing.JPanel;
39  import javax.swing.JScrollPane;
40  import javax.swing.JTextArea;
41  import javax.swing.SwingUtilities;
42  import javax.swing.filechooser.FileFilter;
43  
44  import antlr.ANTLRException;
45  
46  import com.puppycrawl.tools.checkstyle.TreeWalker;
47  import com.puppycrawl.tools.checkstyle.api.DetailAST;
48  import com.puppycrawl.tools.checkstyle.api.FileContents;
49  import com.puppycrawl.tools.checkstyle.api.FileText;
50  
51  /**
52   * Displays information about a parse tree.
53   * The user can change the file that is parsed and displayed
54   * through a JFileChooser.
55   *
56   * @author Lars Kühne
57   */
58  public class ParseTreeInfoPanel extends JPanel
59  {
60      /** For Serialisation that will never happen. */
61      private static final long serialVersionUID = -4243405131202059043L;
62      private final JTreeTable treeTable;
63      private final ParseTreeModel parseTreeModel;
64      private final JTextArea jTextArea;
65      private File lastDirectory = null;
66      private File currentFile = null;
67      private final Action reloadAction;
68      private final List<Integer>   lines2position  = new ArrayList<Integer>();
69  
70      private static class JavaFileFilter extends FileFilter
71      {
72          @Override
73          public boolean accept(File f)
74          {
75              if (f == null) {
76                  return false;
77              }
78              return f.isDirectory() || f.getName().endsWith(".java");
79          }
80  
81          @Override
82          public String getDescription()
83          {
84              return "Java Source Code";
85          }
86      }
87  
88      public void openAst(DetailAST parseTree, final Component parent)
89      {
90          parseTreeModel.setParseTree(parseTree);
91          reloadAction.setEnabled(true);
92  
93          // clear for each new file
94          getLines2position().clear();
95          // starts line counting at 1
96          getLines2position().add(0);
97          // insert the contents of the file to the text area
98  
99          // clean the text area before inserting the lines of the new file
100         if (jTextArea.getText().length() != 0) {
101             jTextArea.replaceRange("", 0, jTextArea.getText().length());
102         }
103 
104         // move back to the top of the file
105         jTextArea.moveCaretPosition(0);
106     }
107 
108     private class FileSelectionAction extends AbstractAction
109     {
110         /**
111          *
112          */
113         private static final long serialVersionUID = -1926935338069418119L;
114 
115         public FileSelectionAction()
116         {
117             super("Select Java File");
118             putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
119         }
120 
121         @Override
122         public void actionPerformed(ActionEvent e)
123         {
124             final JFileChooser fc = new JFileChooser( lastDirectory );
125             final FileFilter filter = new JavaFileFilter();
126             fc.setFileFilter(filter);
127             final Component parent =
128                 SwingUtilities.getRoot(ParseTreeInfoPanel.this);
129             fc.showDialog(parent, "Open");
130             final File file = fc.getSelectedFile();
131             openFile(file, parent);
132 
133         }
134     }
135 
136     private class ReloadAction extends AbstractAction
137     {
138         /**
139          *
140          */
141         private static final long serialVersionUID = -1021880396046355863L;
142 
143         public ReloadAction()
144         {
145             super("Reload Java File");
146             putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);
147         }
148 
149         @Override
150         public void actionPerformed(ActionEvent e)
151         {
152             final Component parent =
153                 SwingUtilities.getRoot(ParseTreeInfoPanel.this);
154             openFile(currentFile, parent);
155         }
156     }
157 
158 
159     private class FileDropListener implements FileDrop.Listener
160     {
161         private final JScrollPane mSp;
162 
163         @Override
164         public void filesDropped(File[] files)
165         {
166             if ((files != null) && (files.length > 0))
167             {
168                 final File file = files[0];
169                 openFile(file, mSp);
170             }
171         }
172 
173         public FileDropListener(JScrollPane aSp)
174         {
175             mSp = aSp;
176         }
177     }
178 
179 
180     public void openFile(File file, final Component parent)
181     {
182         if (file != null) {
183             try {
184                 Main.frame.setTitle("Checkstyle : " + file.getName());
185                 final FileText text = new FileText(file.getAbsoluteFile(),
186                                                    getEncoding());
187                 final DetailAST parseTree = parseFile(text);
188                 parseTreeModel.setParseTree(parseTree);
189                 currentFile = file;
190                 lastDirectory = file.getParentFile();
191                 reloadAction.setEnabled(true);
192 
193                 final String[] sourceLines = text.toLinesArray();
194 
195                 // clear for each new file
196                  getLines2position().clear();
197                  // starts line counting at 1
198                  getLines2position().add(0);
199                  // insert the contents of the file to the text area
200                  for (String element : sourceLines)
201                  {
202                    getLines2position().add(jTextArea.getText().length());
203                    jTextArea.append(element + "\n");
204                  }
205 
206                 //clean the text area before inserting the lines of the new file
207                 if (jTextArea.getText().length() != 0) {
208                     jTextArea.replaceRange("", 0, jTextArea.getText()
209                             .length());
210                 }
211 
212                 // insert the contents of the file to the text area
213                 for (final String element : sourceLines) {
214                     jTextArea.append(element + "\n");
215                 }
216 
217                 // move back to the top of the file
218                 jTextArea.moveCaretPosition(0);
219             }
220             catch (final IOException ex) {
221                 showErrorDialog(
222                         parent,
223                         "Could not open " + file + ": " + ex.getMessage());
224             }
225             catch (final ANTLRException ex) {
226                 showErrorDialog(
227                         parent,
228                         "Could not parse " + file + ": " + ex.getMessage());
229             }
230         }
231     }
232 
233     /**
234      * Parses a file and returns the parse tree.
235      * @param fileName the file to parse
236      * @return the root node of the parse tree
237      * @throws IOException if the file cannot be opened
238      * @throws ANTLRException if the file is not a Java source
239      * @deprecated Use {@link #parseFile(FileText)} instead
240      */
241     @Deprecated
242     public static DetailAST parseFile(String fileName)
243         throws IOException, ANTLRException
244     {
245         return parseFile(new FileText(new File(fileName), getEncoding()));
246     }
247 
248     /**
249      * Parses a file and returns the parse tree.
250      * @param text the file to parse
251      * @return the root node of the parse tree
252      * @throws ANTLRException if the file is not a Java source
253      */
254     public static DetailAST parseFile(FileText text)
255         throws ANTLRException
256     {
257         final FileContents contents = new FileContents(text);
258         return TreeWalker.parse(contents);
259     }
260 
261     /**
262      * Returns the configured file encoding.
263      * This can be set using the {@code file.encoding} system property.
264      * It defaults to UTF-8.
265      * @return the configured file encoding
266      */
267     private static String getEncoding()
268     {
269         return System.getProperty("file.encoding", "UTF-8");
270     }
271 
272     /**
273      * Create a new ParseTreeInfoPanel instance.
274      */
275     public ParseTreeInfoPanel()
276     {
277         setLayout(new BorderLayout());
278 
279         final DetailAST treeRoot = null;
280         parseTreeModel = new ParseTreeModel(treeRoot);
281         treeTable = new JTreeTable(parseTreeModel);
282         final JScrollPane sp = new JScrollPane(treeTable);
283         this.add(sp, BorderLayout.NORTH);
284 
285         final JButton fileSelectionButton =
286             new JButton(new FileSelectionAction());
287 
288         reloadAction = new ReloadAction();
289         reloadAction.setEnabled(false);
290         final JButton reloadButton = new JButton(reloadAction);
291 
292         jTextArea = new JTextArea(20, 15);
293         jTextArea.setEditable(false);
294         treeTable.setEditor(jTextArea);
295         treeTable.setLinePositionMap(lines2position);
296 
297         final JScrollPane sp2 = new JScrollPane(jTextArea);
298         this.add(sp2, BorderLayout.CENTER);
299 
300         final JPanel p = new JPanel(new GridLayout(1,2));
301         this.add(p, BorderLayout.SOUTH);
302         p.add(fileSelectionButton);
303         p.add(reloadButton);
304 
305         try {
306             // TODO: creating an object for the side effect of the constructor
307             // and then ignoring the object looks strange.
308             new FileDrop(sp, new FileDropListener(sp));
309         }
310         catch (final TooManyListenersException ex)
311         {
312            showErrorDialog(null, "Cannot initialize Drag and Drop support");
313         }
314 
315     }
316 
317     private void showErrorDialog(final Component parent, final String msg)
318     {
319         final Runnable showError = new Runnable()
320         {
321             @Override
322             public void run()
323             {
324                 JOptionPane.showMessageDialog(parent, msg);
325             }
326         };
327         SwingUtilities.invokeLater(showError);
328     }
329 
330     public List<Integer> getLines2position()
331     {
332       return lines2position;
333     }
334 }
335