KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > pmd > ViolationProvider


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tasklist.pmd;
21
22 import net.sourceforge.pmd.PMD;
23 import net.sourceforge.pmd.Report;
24 import net.sourceforge.pmd.Rule;
25 import net.sourceforge.pmd.RuleContext;
26 import net.sourceforge.pmd.RuleSet;
27 import net.sourceforge.pmd.RuleViolation;
28 import pmd.*;
29 import pmd.config.ConfigUtils;
30 import pmd.config.PMDOptionsSettings;
31 import java.io.Reader JavaDoc;
32 import java.io.StringReader JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import javax.swing.text.*;
36 import javax.swing.event.*;
37 import java.awt.*;
38 import java.awt.event.*;
39 import javax.swing.*;
40 import java.util.List JavaDoc;
41 import org.openide.cookies.SourceCookie;
42 import org.openide.explorer.view.*;
43 import org.openide.nodes.*;
44 import org.openide.ErrorManager;
45 import org.openide.loaders.DataObject;
46 import org.openide.loaders.DataObjectNotFoundException;
47 import org.openide.text.Line;
48 import org.openide.util.NbBundle;
49 import org.openide.util.Utilities;
50 import org.openide.text.DataEditorSupport;
51
52 import org.netbeans.modules.tasklist.core.TLUtils;
53 import org.netbeans.modules.tasklist.client.*;
54 import org.netbeans.modules.tasklist.providers.DocumentSuggestionProvider;
55 import org.netbeans.modules.tasklist.providers.SuggestionContext;
56 import org.openide.src.ClassElement;
57 import org.openide.src.Identifier;
58
59 /**
60  * This class uses the PMD rule checker to provide rule violation
61  * suggestions.
62  * <p>
63  * @todo Add more automatic fixers for rules.
64  * Potentially easy: UnusedModifier.
65  * Other candidates: StringToString, StringInstantiation, ...
66  * MustUseBraces?
67  * <p>
68  * @author Tor Norbye
69  */

70
71
72 public class ViolationProvider extends DocumentSuggestionProvider {
73
74     final private static String JavaDoc TYPE = "pmd-violations"; // NOI18N
75
private SuggestionContext env;
76
77     public String JavaDoc getType() {
78         return TYPE;
79     }
80
81     private Thread JavaDoc last;
82
83     public List JavaDoc scan(SuggestionContext env) {
84         assert last == null || last == Thread.currentThread() : "Concurent access by: " + last + " and: " + Thread.currentThread();
85         last = Thread.currentThread();
86         try {
87 // System.err.println("\nPMD.scan[" + ((env != null) ? env.getFileObject().toString() : "null") + "]: " + showingTasks );
88

89             List JavaDoc tasks = null;
90             try {
91
92             SuggestionManager manager = SuggestionManager.getDefault();
93             if (!manager.isEnabled(TYPE)) {
94                 return null;
95             }
96
97             DataObject dataObject = DataObject.find(env.getFileObject());
98             SourceCookie cookie =
99                 (SourceCookie)dataObject.getCookie(SourceCookie.class);
100
101             // The file is not a java file
102
if(cookie == null) {
103                 return null;
104             }
105
106             String JavaDoc text = env.getCharSequence().toString();
107             Reader JavaDoc reader = new StringReader JavaDoc(text);
108             // XXX got an unexplained NPE in here somewhere...
109
ClassElement[] topClazzes = cookie.getSource().getClasses();
110             if (topClazzes.length == 0) {
111                 // Empty file, skip.
112
return null;
113             }
114             assert topClazzes[0] != null : cookie.getSource().getClass().getName();
115             Identifier topClazzName = topClazzes[0].getName();
116             assert topClazzName != null : topClazzes[0].getClass().getName();
117             String JavaDoc name = topClazzName.getFullName();
118             PMD pmd = new PMD();
119             RuleContext ctx = new RuleContext();
120             Report report = new Report();
121             ctx.setReport(report);
122             ctx.setSourceCodeFilename(name);
123
124             RuleSet set = new RuleSet();
125             List JavaDoc rlist = ConfigUtils.createRuleList(
126                                  PMDOptionsSettings.getDefault().getRules());
127             Iterator JavaDoc it = rlist.iterator();
128             while(it.hasNext()) {
129                 set.addRule((Rule)it.next());
130             }
131             try {
132                 pmd.processFile(reader, set, ctx);
133             } catch (Exception JavaDoc e) {
134                 // For some reason, some of the PMD classes
135
// throw RuntimeExceptions or PMDExceptions.
136
// I suspect PMD wasn't written with the intent of it being run
137
// on incomplete or invalid classes. So we just swallow the
138
// exceptions here
139
; // Avoid PMD warning about empty catch block - this is intentional
140
} catch (Error JavaDoc e) {
141                 // Ditto. It throws some non-exceptions like TokenMgrError
142
; // Avoid PMD warning about empty catch block - this is intentional
143
}
144             Iterator JavaDoc iterator = ctx.getReport().iterator();
145
146             Image taskIcon = Utilities.loadImage("org/netbeans/modules/tasklist/pmd/fixable.gif"); // NOI18N
147

148             if(!ctx.getReport().isEmpty()) {
149                 while(iterator.hasNext()) {
150                     final RuleViolation violation = (RuleViolation)iterator.next();
151                     try {
152                         // Violation line numbers seem to be 0-based
153
final Line line = TLUtils.getLineByNumber(dataObject, violation.getLine());
154
155                         //System.out.println("Next violation = " + violation.getRule().getName() + " with description " + violation.getDescription() + " on line " + violation.getLine());
156

157                         boolean fixable = false;
158                         SuggestionPerformer action = null;
159                         String JavaDoc rulename = violation.getRule().getName();
160                         if (rulename.equals("UnusedImports") || // NOI18N
161
rulename.equals("ImportFromSamePackage") || // NOI18N
162
rulename.equals("DontImportJavaLang") || // NOI18N
163
rulename.equals("DuplicateImports")) { // NOI18N
164
fixable = true;
165                             boolean comment = false;
166                             action = new ImportPerformer(line, violation, comment);
167                         } else if (rulename.equals("UnusedLocalVariable") && // NOI18N
168
isDeleteSafe(line)) { // only a check
169
fixable = true;
170                             action = new SuggestionPerformer() {
171                                 public void perform(Suggestion s) {
172                                     // Remove the particular line
173
TLUtils.deleteLine(line, "");
174                                 }
175                                 public boolean hasConfirmation() {
176                                     return true;
177                                 }
178                                 public Object JavaDoc getConfirmation(Suggestion s) {
179                                     DataObject dao = DataEditorSupport.findDataObject(line);
180                                     int linenumber = line.getLineNumber();
181                                     String JavaDoc filename = dao.getPrimaryFile().getNameExt();
182                                     String JavaDoc ruleDesc = violation.getRule().getDescription();
183                                     String JavaDoc ruleExample = violation.getRule().getExample();
184                                     String JavaDoc beforeDesc = NbBundle.getMessage(ViolationProvider.class,
185                                             "UnusedConfirmation"); // NOI18N
186

187                                     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(200);
188                                     Line l = line;
189                                     sb.append("<html>"); // NOI18N
190
TLUtils.appendSurroundingLine(sb, l, -1);
191                                     sb.append("<br>");
192                                     sb.append("<b><strike>");
193                                     sb.append(line.getText());
194                                     sb.append("</strike></b>");
195                                     sb.append("<br>");
196                                     TLUtils.appendSurroundingLine(sb, l, +1);
197                                     sb.append("</html>"); // NOI18N
198
String JavaDoc beforeContents = sb.toString();
199
200                                     return new org.netbeans.modules.tasklist.core.ConfPanel(beforeDesc,
201                                           beforeContents, null, null,
202                                           filename, linenumber, getBottomPanel(ruleDesc, ruleExample));
203                                 }
204                             };
205
206                         } else if (rulename.equals("UnusedPrivateField")) { // NOI18N
207
fixable = true;
208                             boolean comment = false;
209                             action = new RemovePerformer(true,
210                                                          line, violation,
211                                                          comment);
212                         } else if (rulename.equals("UnusedPrivateMethod")) { // NOI18N
213
fixable = true;
214                             boolean comment = false;
215                             action = new RemovePerformer(false,
216                                                          line, violation,
217                                                          comment);
218                         } else {
219                             action = null;
220                         }
221
222                         SuggestionAgent s = manager.createSuggestion(
223                             TYPE,
224                             rulename + " : " + // NOI18N
225
violation.getDescription(),
226                             action,
227                             this);
228
229                         // Make sure PMD's rule range is still the same
230
// as ours. If not, we've gotta scale it
231
// JDK14: assert Rule.LOWEST_PRIORITY == 5;
232
switch (violation.getRule().getPriority()) {
233                         case 1: s.setPriority(SuggestionPriority.HIGH); break;
234                         case 2: s.setPriority(SuggestionPriority.MEDIUM_HIGH); break;
235                         case 3: s.setPriority(SuggestionPriority.MEDIUM); break;
236                         case 4: s.setPriority(SuggestionPriority.MEDIUM_LOW); break;
237                         case 5: s.setPriority(SuggestionPriority.LOW); break;
238                         default: s.setPriority(SuggestionPriority.MEDIUM); break;
239                         }
240
241                         s.setLine(line);
242                         if (fixable) {
243                             s.setIcon(taskIcon);
244                         }
245                         if (tasks == null) {
246                             tasks = new ArrayList JavaDoc(ctx.getReport().size());
247                         }
248                         tasks.add(s.getSuggestion());
249                     } catch (Exception JavaDoc e) {
250                         ErrorManager.getDefault().notify(e);
251                     }
252                 }
253             }
254             } catch (Exception JavaDoc e) {
255                 ErrorManager.getDefault().notify(e);
256             }
257             return tasks;
258         } finally {
259             last = null;
260         }
261     }
262     
263     /**
264      * Checks designed to prevent deleting additional content on the
265      * line - for example, it won't delete anything if it detects
266      * multiple statements on the line, or function calls. It may err
267      * on the safe side; e.g. not delete even when it would be safe to do so.
268      */

269     private static boolean isDeleteSafe(String JavaDoc text) {
270         /*
271           What about a weird corner case like this:
272           int z = 0;
273           for (int y = 0;
274           z < 5;
275           z++) {
276           importantCall();
277           }
278           Will I delete the "for(int y = 0" line since y is unused?
279
280           No - because I will see the "(" and bail!
281         */

282
283         // A small statemachine to figure out if the line can
284
// be "safely" deleted
285
int n = text.length();
286         boolean inString = false;
287         boolean escaped = false;
288
289         // What do we initialize comment too? It's POSSIBLE that you
290
// have code like this
291
// /* Begin comment:
292
// end */ int unused = 5;
293
// ...and I begin in the middle of a comment. But I think this
294
// is an unusual scenario...
295
boolean comment = false;
296                 
297         boolean seenSemi = false;
298         boolean seenComma = false;
299         for (int i = 0; i < n; i++) {
300             char c = text.charAt(i);
301             if (comment) {
302                 if ((c == '*') && (i < (n-1)) &&
303                     ((text.charAt(i+1) == '/'))) {
304                     comment = false;
305                 } else {
306                     continue;
307                 }
308             } else if (c == '\\') {
309                 escaped = !escaped;
310             } else if (c == '"') {
311                 if (!escaped) {
312                     inString = !inString;
313                 }
314             } else if ((c == '/') && (i < (n-1)) &&
315                        ((text.charAt(i+1) == '*'))) {
316                 comment = true;
317             } else if (c == '(') {
318                 if (!inString && !escaped) {
319                     // BAIL! "(" on a line makes me nervous, e.g. unused
320
// variable "success" in
321
// boolean success = saveData();
322
//System.out.println("BAILING: function call on the line!");
323
return false;
324                 }
325             } else if (c == ',') {
326                 if (!inString && !escaped) {
327                     seenComma = true;
328                 }
329             } else if (c == ';') {
330                 if (!inString && !escaped) {
331                     seenSemi = true;
332                 }
333             } else if (Character.isWhitespace(c)) {
334                 // do nothing
335
} else {
336                 // Some other character
337
if (!inString && !escaped && (seenSemi || seenComma)) {
338                     // BAIL -- we've seen text after a semicolon or
339
// comma - multiple statements on the line!
340
//System.out.println("BAILING: character after semi=" + seenSemi + " or comma=" + seenComma);
341
return false;
342                 }
343             }
344         }
345         return true;
346     }
347     
348     /** Check if line/field deletion is safe. Safe here means
349      * that the field does not get assigned some method (which may
350      * have an important side effect)
351      */

352     public static boolean isDeleteSafe(Line line) {
353         Document doc = TLUtils.getDocument(line);
354         Element elm = TLUtils.getElement(doc, line);
355         if (elm == null) {
356             return false;
357         }
358         int offset = elm.getStartOffset();
359         int endOffset = elm.getEndOffset();
360
361         try {
362             String JavaDoc text = doc.getText(offset, endOffset-offset);
363             return isDeleteSafe(text);
364         } catch (BadLocationException ex) {
365             ErrorManager.getDefault().notify(ErrorManager.WARNING, ex);
366         }
367         return false;
368     }
369     
370     static JPanel getBottomPanel(String JavaDoc ruleDesc, String JavaDoc ruleExample) {
371         java.awt.GridBagConstraints JavaDoc gridBagConstraints;
372         // Variables declaration - do not modify
373
javax.swing.JLabel JavaDoc jLabel9;
374         javax.swing.JLabel JavaDoc jLabel8;
375         javax.swing.JScrollPane JavaDoc jScrollPane2;
376         javax.swing.JTextArea JavaDoc descText;
377         javax.swing.JScrollPane JavaDoc jScrollPane1;
378         javax.swing.JPanel JavaDoc jPanel1;
379         javax.swing.JTextArea JavaDoc exampleText;
380         // End of variables declaration
381

382         
383         jPanel1 = new javax.swing.JPanel JavaDoc();
384         jLabel8 = new javax.swing.JLabel JavaDoc();
385         jScrollPane1 = new javax.swing.JScrollPane JavaDoc();
386         descText = new javax.swing.JTextArea JavaDoc();
387         jLabel9 = new javax.swing.JLabel JavaDoc();
388         jScrollPane2 = new javax.swing.JScrollPane JavaDoc();
389         exampleText = new javax.swing.JTextArea JavaDoc();
390
391         jPanel1.setLayout(new java.awt.GridBagLayout JavaDoc());
392
393         jLabel8.setText(NbBundle.getMessage(ViolationProvider.class, "Description")); // NOI18N();
394
gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
395         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
396         jPanel1.add(jLabel8, gridBagConstraints);
397
398         jScrollPane1.setPreferredSize(new java.awt.Dimension JavaDoc(200, 200));
399         descText.setWrapStyleWord(true);
400         descText.setLineWrap(true);
401         descText.setEditable(false);
402         jScrollPane1.setViewportView(descText);
403
404         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
405         gridBagConstraints.gridx = 0;
406         gridBagConstraints.gridy = 1;
407         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
408         gridBagConstraints.weightx = 1.0;
409         gridBagConstraints.weighty = 1.0;
410         jPanel1.add(jScrollPane1, gridBagConstraints);
411
412         jLabel9.setText(NbBundle.getMessage(ViolationProvider.class, "Example")); // NOI18N();
413
gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
414         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
415         jPanel1.add(jLabel9, gridBagConstraints);
416
417         jScrollPane2.setPreferredSize(new java.awt.Dimension JavaDoc(200, 200));
418         exampleText.setEditable(false);
419         exampleText.setPreferredSize(null);
420         jScrollPane2.setViewportView(exampleText);
421
422         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
423         gridBagConstraints.gridx = 1;
424         gridBagConstraints.gridy = 1;
425         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
426         gridBagConstraints.weightx = 1.0;
427         gridBagConstraints.weighty = 1.0;
428         jPanel1.add(jScrollPane2, gridBagConstraints);
429
430         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
431         gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
432         gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
433         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
434         gridBagConstraints.weightx = 1.0;
435         gridBagConstraints.weighty = 1.0;
436         gridBagConstraints.insets = new java.awt.Insets JavaDoc(18, 12, 11, 11);
437
438         // Dont' use monospaced fonts
439
descText.setFont(jLabel8.getFont());
440         exampleText.setFont(jLabel8.getFont());
441         
442         descText.setText(ruleDesc.trim());
443         exampleText.setText(ruleExample.trim());
444         
445         return jPanel1;
446     }
447
448     /** The list of tasks we're currently showing in the tasklist */
449     private List JavaDoc showingTasks = null;
450
451     private Object JavaDoc request = null;
452 }
453
Popular Tags