001: package org.acm.seguin.ide.common;
002:
003: import java.awt.BorderLayout;
004: import java.awt.Frame;
005: import java.awt.Component;
006: import java.awt.event.ActionEvent;
007: import java.awt.event.ActionListener;
008: import java.io.File;
009: import java.io.FileInputStream;
010: import java.io.FileNotFoundException;
011: import java.util.ArrayList;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Enumeration;
015:
016: import javax.swing.JButton;
017: import javax.swing.JOptionPane;
018: import javax.swing.JPanel;
019: import javax.swing.JScrollPane;
020: import javax.swing.JTree;
021: import javax.swing.JLabel;
022: import javax.swing.event.TreeSelectionEvent;
023: import javax.swing.event.TreeSelectionListener;
024: import javax.swing.tree.DefaultMutableTreeNode;
025: import javax.swing.tree.DefaultTreeModel;
026: import javax.swing.tree.TreeSelectionModel;
027: import javax.swing.tree.DefaultTreeCellRenderer;
028: import org.acm.seguin.ide.common.options.SelectedRules;
029: import net.sourceforge.jrefactory.action.GenericAction;
030: import net.sourceforge.jrefactory.action.RenameMethodAction;
031: import net.sourceforge.jrefactory.action.RenameFieldAction;
032:
033: import org.acm.seguin.pmd.PMD;
034: import org.acm.seguin.pmd.PMDException;
035: import org.acm.seguin.pmd.Report;
036: import org.acm.seguin.pmd.RuleContext;
037: import org.acm.seguin.pmd.RuleSetNotFoundException;
038: import org.acm.seguin.pmd.RuleViolation;
039:
040: /**
041: * A GUI Component to display Duplicate code.
042: *
043: * @author Jiger Patel
044: * @author Mike Atkinson
045: * @created 05 Apr 2003
046: */
047:
048: public class CodingStandardsViewer extends JPanel {
049: Frame view;
050: JTree tree;
051: DefaultTreeModel treeModel = new DefaultTreeModel(
052: new DefaultMutableTreeNode(
053: "Coding Standard Checking Results", true));
054: private final static String CURRENT_BUFFER = "Current Buffer";
055:
056: /**
057: * Constructor for the CodingStandardsViewer object
058: *
059: * @param aView Description of Parameter
060: */
061: public CodingStandardsViewer(Frame aView) {
062: this .view = aView;
063: setLayout(new BorderLayout());
064: JButton checkBuff = new JButton("Current");
065: checkBuff.addActionListener(new ActionListener() {
066: public void actionPerformed(ActionEvent evt) {
067: IDEPlugin.checkBuffer(view, null);
068: }
069: });
070: JButton checkAllBuff = new JButton("All Buffers");
071: checkAllBuff.addActionListener(new ActionListener() {
072: public void actionPerformed(ActionEvent evt) {
073: IDEPlugin.checkAllOpenBuffers(view);
074: }
075: });
076: JButton checkCurDir = new JButton("Dir");
077: checkCurDir.addActionListener(new ActionListener() {
078: public void actionPerformed(ActionEvent evt) {
079: IDEPlugin.checkDirectory(view, false);
080: }
081: });
082: JButton checkDirRecurse = new JButton("Subdirs");
083: checkDirRecurse.addActionListener(new ActionListener() {
084: public void actionPerformed(ActionEvent evt) {
085: IDEPlugin.checkDirectory(view, true);
086: }
087: });
088: JButton clearbutton = new JButton(IDEPlugin
089: .loadIcon("Clear.png"));
090: clearbutton.setBorderPainted(false);
091: clearbutton.setToolTipText(IDEPlugin
092: .getProperty("javastyle.pmd.clear.label"));
093: clearbutton.addActionListener(new ActionListener() {
094: public void actionPerformed(ActionEvent evt) {
095: clearViolations();
096: refreshTree();
097: }
098: });
099: final JPanel top = new JPanel();
100: top.setLayout(new BorderLayout());
101:
102: final JPanel buttons = new JPanel();
103: buttons.setLayout(new java.awt.FlowLayout());
104: buttons.add(checkBuff);
105: buttons.add(checkAllBuff);
106: buttons.add(checkCurDir);
107: buttons.add(checkDirRecurse);
108: buttons.add(clearbutton);
109: top.add(new javax.swing.JLabel("check coding standards in:"),
110: BorderLayout.NORTH);
111: top.add(buttons, BorderLayout.CENTER);
112: add(top, BorderLayout.NORTH);
113:
114: tree = new JTree(treeModel);
115: tree.setCellRenderer(new CSRenderer());
116: tree.getSelectionModel().setSelectionMode(
117: TreeSelectionModel.SINGLE_TREE_SELECTION);
118: tree.addTreeSelectionListener(new TreeSelectionListener() {
119: public void valueChanged(TreeSelectionEvent evt) {
120: DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree
121: .getLastSelectedPathComponent();
122:
123: if (node != null) {
124: System.out.println("node=" + node);
125: System.out
126: .println("node.isLeaf()=" + node.isLeaf());
127: System.out.println("node.getClass()="
128: + node.getClass());
129: if (node.isLeaf() && node instanceof Refactor) {
130: System.out.println("trying to refactor");
131: try {
132: Refactor refactor = (Refactor) node;
133: (new RunGotoViolation(refactor, view))
134: .run();
135: final Object buffer = IDEPlugin.openFile(
136: view, refactor.getFilename());
137: IDEPlugin.setBuffer(view, buffer);
138: System.out
139: .println("trying to refactor - before running refactoring action");
140: refactor.getAction().actionPerformed(
141: new java.awt.event.ActionEvent(
142: buffer, refactor.getLine(),
143: "???"));
144: System.out
145: .println("trying to refactor - after running refactoring action");
146: } catch (java.io.IOException e) {
147: e.printStackTrace();
148: }
149: } else if (node.isLeaf()
150: && node instanceof Violation) {
151: Object obj = node.getUserObject();
152: Violation violation = (Violation) node;
153: if (obj == null) {
154: (new RunGotoViolation(violation, view))
155: .run();
156: } else {
157: try {
158: (new RunGotoViolation(violation, view))
159: .run();
160: final Object buffer = IDEPlugin
161: .openFile(view, violation
162: .getFilename());
163: IDEPlugin.setBuffer(view, buffer);
164: ((GenericAction) obj)
165: .actionPerformed(new java.awt.event.ActionEvent(
166: buffer, violation
167: .getLine(),
168: "???"));
169: } catch (java.io.IOException e) {
170: e.printStackTrace();
171: }
172: }
173: }
174: }
175: }
176: });
177:
178: add(new JScrollPane(tree), BorderLayout.CENTER);
179: }
180:
181: public void setView(Frame view) {
182: this .view = view;
183: }
184:
185: /**
186: * Gets the root attribute of the CPDDuplicateCodeViewer object
187: *
188: * @return The root value
189: */
190: public DefaultMutableTreeNode getRoot() {
191: return (DefaultMutableTreeNode) treeModel.getRoot();
192: }
193:
194: /**
195: * Description of the Method
196: *
197: * @param view Description of Parameter
198: * @param buffer Description of Parameter
199: * @return Description of the Returned Value
200: */
201: public RuleContext check(Frame view, Object buffer, boolean silent) {
202: //System.err.println("check(view, " + buffer + ")");
203: RuleContext ctx = new RuleContext();
204: ctx.setReport(new Report());
205: ctx.setSourceCodeFilename("");
206: IDEPlugin.clearAnnotations(view, buffer,
207: IDEInterface.CODING_STANDARDS);
208:
209: try {
210: PMD pmd = new PMD();
211: SelectedRules selectedRuleSets = new SelectedRules(
212: IDEPlugin.getProjectName(view, buffer), view);
213: pmd.processFile(new java.io.StringReader(IDEPlugin.getText(
214: view, buffer)),
215: selectedRuleSets.getSelectedRules(), ctx);
216: String path = IDEPlugin.getFilePathForBuffer(buffer);
217: if (ctx.getReport().isEmpty()) {
218: if (!silent) {
219: JOptionPane.showMessageDialog(this ,
220: "No problems found (1)", "JRefactory",
221: JOptionPane.INFORMATION_MESSAGE);
222: }
223: removeViolations((path == null) ? "" : path);
224: } else {
225: Violations violations = (path != null && path.length() > 0) ? new Violations(
226: path, path)
227: : new Violations(path, "");
228:
229: for (Iterator i = ctx.getReport().iterator(); i
230: .hasNext();) {
231: RuleViolation ruleViolation = (RuleViolation) i
232: .next();
233: //System.out.println("ruleViolation=" + ruleViolation.getLine() + " : " + ruleViolation.getDescription());
234: Violation violation = new Violation(path,
235: ruleViolation.getLine(), ruleViolation
236: .getDescription());
237:
238: violations.addViolation(violation);
239: IDEPlugin.addAnnotation(view, buffer,
240: IDEInterface.CODING_STANDARDS,
241: ruleViolation.getLine(), ruleViolation
242: .getDescription());
243: }
244: addViolations(violations);
245: }
246: refreshTree();
247: expandAll();
248: } catch (RuleSetNotFoundException rsne) {
249: rsne.printStackTrace();
250: } catch (PMDException pmde) {
251: pmde.printStackTrace();
252: JOptionPane.showMessageDialog(this ,
253: "Error while processing " + "<no path>");
254: }
255: return ctx;
256: }
257:
258: /**
259: * Description of the Method
260: *
261: * @param files Description of Parameter
262: * @param view Description of Parameter
263: * @param buffer Description of Parameter
264: * @return Description of the Returned Value
265: */
266: public List checkFiles(List files, Frame view, Object buffer) {
267: PMD pmd = new PMD();
268: SelectedRules selectedRuleSets = null;
269: IDEPlugin.clearAnnotations(view, null,
270: IDEInterface.CUT_AND_PASTE_DETECTOR);
271:
272: try {
273: selectedRuleSets = new SelectedRules(IDEPlugin
274: .getProjectName(view, buffer), view);
275: } catch (RuleSetNotFoundException rsne) {
276: // should never happen since rulesets are fetched via getRegisteredRuleSet, nonetheless:
277: System.out
278: .println("JavaStyle ERROR: Couldn't find a ruleset");
279: rsne.printStackTrace();
280: JOptionPane
281: .showMessageDialog(
282: view,
283: "Unable to find rulesets, halting Coding Standards Check",
284: "JRefactory", JOptionPane.ERROR_MESSAGE);
285: return new ArrayList();
286: }
287:
288: List contexts = new ArrayList();
289: boolean foundProblems = false;
290:
291: for (Iterator i = files.iterator(); i.hasNext();) {
292: File file = (File) i.next();
293:
294: RuleContext ctx = new RuleContext();
295: contexts.add(ctx);
296: ctx.setReport(new Report());
297: ctx.setSourceCodeFilename(file.getAbsolutePath());
298: try {
299: pmd.processFile(new FileInputStream(file),
300: selectedRuleSets.getSelectedRules(), ctx);
301: } catch (FileNotFoundException fnfe) {
302: // should never happen, but if it does, carry on to the next file
303: System.out
304: .println("JavaStyle ERROR: Unable to open file "
305: + file.getAbsolutePath());
306: } catch (PMDException pmde) {
307: pmde.printStackTrace();
308: JOptionPane.showMessageDialog(view,
309: "Error while processing "
310: + file.getAbsolutePath());
311: }
312:
313: String path = file.getAbsolutePath();
314: if (ctx.getReport().size() > 0) {
315: Violations violations = new Violations(path, path);
316: for (Iterator j = ctx.getReport().iterator(); j
317: .hasNext();) {
318: foundProblems = true;
319: RuleViolation ruleViolation = (RuleViolation) j
320: .next();
321: //errorSource.addError(ErrorSource.WARNING, path, ruleViolation.getLine() - 1, 0, 0, ruleViolation.getDescription());
322: Violation violation = new Violation(path,
323: ruleViolation.getLine(), ruleViolation
324: .getDescription());
325: violations.addViolation(violation);
326: IDEPlugin.addAnnotation(view, path,
327: IDEInterface.CUT_AND_PASTE_DETECTOR,
328: ruleViolation.getLine(), ruleViolation
329: .getDescription());
330: }
331: addViolations(violations);
332: } else {
333: removeViolations(path);
334: }
335: refreshTree();
336: }
337: refreshTree();
338: expandAll();
339: if (!foundProblems) {
340: JOptionPane.showMessageDialog(view, "No problems found",
341: "Jrefactory", JOptionPane.INFORMATION_MESSAGE);
342: }
343: return contexts;
344: }
345:
346: /** Description of the Method */
347: public void refreshTree() {
348: treeModel.reload();
349: }
350:
351: /**
352: * Description of the Method
353: *
354: * @param violation Description of Parameter
355: */
356: public void gotoViolation(final Violation violation) {
357: if (violation != null) {
358: IDEPlugin.runInAWTThread(new RunGotoViolation(violation,
359: view));
360: }
361: }
362:
363: /**
364: * Adds a feature to the Duplicates attribute of the CPDDuplicateCodeViewer object
365: *
366: * @param violations The feature to be added to the Violations attribute
367: */
368: public void addViolations(Violations violations) {
369: Enumeration children = getRoot().children();
370: while (children.hasMoreElements()) {
371: Violations v = (Violations) children.nextElement();
372: if (v.sourcecode.equals(violations.sourcecode)) {
373: v.removeAllChildren();
374: int limit = violations.getChildCount();
375: for (int i = 0; i < limit; i++) {
376: Violation viol = (Violation) violations
377: .getChildAt(0);
378: v.addViolation(viol);
379: }
380: v.message = violations.message;
381: v.sourcecode = violations.sourcecode;
382: if (!v.message.equals(CURRENT_BUFFER)) {
383: // remove the old current buffer violations if the current buffer has changed.
384: removeCurrentBufferViolations();
385: }
386: return;
387: }
388: }
389: if (!violations.message.equals(CURRENT_BUFFER)) {
390: // remove the old current buffer violations if the current buffer has changed.
391: removeCurrentBufferViolations();
392: }
393: getRoot().add(violations);
394: }
395:
396: /**
397: * Adds a feature to the Duplicates attribute of the CPDDuplicateCodeViewer object
398: *
399: * @param path Description of Parameter
400: */
401: public void removeViolations(String path) {
402: Enumeration children = getRoot().children();
403: while (children.hasMoreElements()) {
404: Violations v = (Violations) children.nextElement();
405: if (v.sourcecode.equals(path)) {
406: getRoot().remove(v);
407: break;
408: }
409: }
410: removeCurrentBufferViolations();
411: }
412:
413: /** Description of the Method */
414: private void removeCurrentBufferViolations() {
415: Enumeration children = getRoot().children();
416: while (children.hasMoreElements()) {
417: Violations v = (Violations) children.nextElement();
418: if (v.message.equals(CURRENT_BUFFER)) {
419: getRoot().remove(v);
420: return;
421: }
422: }
423: }
424:
425: /** Description of the Method */
426: public void expandAll() {
427: int row = 0;
428:
429: while (row < tree.getRowCount()) {
430: tree.expandRow(row);
431: row++;
432: }
433: }
434:
435: /** Description of the Method */
436: public void collapseAll() {
437: int row = tree.getRowCount() - 1;
438:
439: while (row >= 0) {
440: tree.collapseRow(row);
441: row--;
442: }
443: }
444:
445: /** Description of the Method */
446: public void clearViolations() {
447: getRoot().removeAllChildren();
448: IDEPlugin.clearAnnotations(view, null,
449: IDEInterface.CODING_STANDARDS);
450: }
451:
452: /**
453: * Description of the Class
454: *
455: * @author Mike Atkinson
456: * @created September 13, 2003
457: */
458: public class Violations extends DefaultMutableTreeNode {
459: String message, sourcecode;
460:
461: /**
462: * Constructor for the Duplicates object
463: *
464: * @param message Description of Parameter
465: * @param sourcecode Description of Parameter
466: */
467: public Violations(String message, String sourcecode) {
468: this .message = message;
469: this .sourcecode = sourcecode;
470: }
471:
472: /**
473: * Gets the sourceCode attribute of the Duplicates object
474: *
475: * @return The sourceCode value
476: */
477: public String getSourceCode() {
478: return sourcecode;
479: }
480:
481: /**
482: * Adds a feature to the Duplicate attribute of the Duplicates object
483: *
484: * @param violation The feature to be added to the Violation attribute
485: */
486: public void addViolation(Violation violation) {
487: add(violation);
488: }
489:
490: /**
491: * Description of the Method
492: *
493: * @return Description of the Return Value
494: */
495: public String toString() {
496: return message;
497: }
498: }
499:
500: /**
501: * This holds the information about a coding standards violation in the tree view.
502: *
503: * @author Mike Atkinson
504: * @created September 13, 2003
505: */
506: public class Violation extends DefaultMutableTreeNode {
507: private String filename;
508: private int line;
509: private String description;
510:
511: /**
512: * Constructor for the Duplicate object
513: *
514: * @param filename Description of Parameter
515: * @param line Description of Parameter
516: * @param description Description of Parameter
517: */
518: public Violation(String filename, int line, String description) {
519: this .filename = filename;
520: this .line = line;
521: this .description = description;
522: if (description
523: .startsWith("Avoid using short method names")) {
524: //setUserObject(new RenameMethodAction());
525: add(new Violation(filename, line, "go to line"));
526: add(new Refactor(filename, line,
527: new RenameMethodAction()));
528: } else if (description
529: .startsWith("Method name does not begin with a lower case character")) {
530: //setUserObject(new RenameMethodAction());
531: add(new Violation(filename, line, "go to line"));
532: add(new Refactor(filename, line,
533: new RenameMethodAction()));
534: } else if (description
535: .startsWith("Avoid variables with short names like")) {
536: //setUserObject(new RenameFieldAction());
537: add(new Violation(filename, line, "go to line"));
538: add(new Refactor(filename, line,
539: new RenameFieldAction()));
540: } else if (description
541: .startsWith("Avoid excessively long variable names like")) {
542: //setUserObject(new RenameFieldAction());
543: add(new Violation(filename, line, "go to line"));
544: add(new Refactor(filename, line,
545: new RenameFieldAction()));
546: }
547: }
548:
549: /**
550: * Gets the filename attribute of the Duplicate object
551: *
552: * @return The filename value
553: */
554: public String getFilename() {
555: return filename;
556: }
557:
558: /**
559: * Gets the beginLine attribute of the Duplicate object
560: *
561: * @return The beginLine value
562: */
563: public int getLine() {
564: return line;
565: }
566:
567: /**
568: * Gets the endLine attribute of the Duplicate object
569: *
570: * @return The endLine value
571: */
572: public String getDescription() {
573: return description;
574: }
575:
576: /**
577: * Description of the Method
578: *
579: * @return Description of the Return Value
580: */
581: public String toString() {
582: return getDescription() + " (" + filename + ":" + getLine()
583: + ")";
584: }
585: }
586:
587: /**
588: * This holds the information about a refactoring action to correct a coding standards
589: * violation, within in the tree view.
590: *
591: * @author Mike Atkinson
592: * @created May 06, 2004
593: */
594: public class Refactor extends Violation {
595: private GenericAction action;
596:
597: public Refactor(String filename, int line, GenericAction action) {
598: super (filename, line, action.getValue(GenericAction.NAME)
599: .toString());
600: this .action = action;
601: }
602:
603: /**
604: * Gets the endLine attribute of the Duplicate object
605: *
606: * @return The endLine value
607: */
608: public GenericAction getAction() {
609: return action;
610: }
611:
612: /**
613: * Description of the Method
614: *
615: * @return Description of the Return Value
616: */
617: public String toString() {
618: return "<html><b>Refactor: " + getDescription()
619: + "</b></html>";
620: }
621: }
622:
623: /**
624: * This class is used to jump to the position in the Java source where coding standards violation occurred
625: *
626: * @author Mike Atkinson
627: */
628: private static class RunGotoViolation implements Runnable {
629: private Violation violation;
630: private Frame view;
631:
632: /**
633: * Constructor for the RunGotoViolation object
634: *
635: * @param violation Description of Parameter
636: * @param view Description of Parameter
637: */
638: public RunGotoViolation(Violation violation, Frame view) {
639: this .violation = violation;
640: this .view = view;
641: }
642:
643: /** Main processing method for the RunGotoViolation object */
644: public void run() {
645: try {
646: final Object buffer = IDEPlugin.openFile(view,
647: violation.getFilename());
648: IDEPlugin.setBuffer(view, buffer);
649: int start = IDEPlugin.getLineStartOffset(buffer,
650: violation.getLine() - 1);
651: int end = IDEPlugin.getLineStartOffset(buffer,
652: violation.getLine());
653: IDEPlugin.setSelection(view, buffer, start, end);
654: IDEPlugin.moveCaretPosition(view, buffer, end);
655: } catch (Exception ex) {
656: ex.printStackTrace();
657: IDEPlugin
658: .log(IDEInterface.ERROR,
659: "CodingStandardsViewer",
660: "can't open duplicate file! "
661: + ex.getMessage());
662: }
663: }
664: }
665:
666: /**
667: * Custom cell renderer for the bug tree.
668: * We use this to select the tree icons, and to set the
669: * text color based on the bug priority.
670: */
671: private static class CSRenderer extends DefaultTreeCellRenderer {
672: public CSRenderer() {
673: }
674:
675: public Component getTreeCellRendererComponent(JTree tree,
676: Object value, boolean sel, boolean expanded,
677: boolean leaf, int row, boolean hasFocus) {
678: DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
679: Object obj = node.getUserObject();
680:
681: Component c = super .getTreeCellRendererComponent(tree,
682: value, sel, expanded, leaf, row, hasFocus);
683: if (!leaf || obj == null || !(obj instanceof GenericAction)) {
684: return c;
685: }
686: java.awt.Color color = c.getBackground();
687: JPanel panel = new JPanel();
688: panel.setBackground(color);
689: panel.setLayout(new BorderLayout());
690: panel.add(c, BorderLayout.NORTH);
691: Object o = ((GenericAction) obj)
692: .getValue(GenericAction.NAME);
693: JLabel c2 = new JLabel(" ");
694: panel.add(c2, BorderLayout.WEST);
695: JLabel c3 = new JLabel("Refactor: " + o);
696: panel.add(c3, BorderLayout.CENTER);
697: panel.validate();
698: panel.setMinimumSize(panel.getMinimumSize());
699: return panel;
700: }
701: }
702:
703: }
|