001: package tide.exttools.PMD;
002:
003: import java.util.zip.ZipEntry;
004: import java.util.zip.ZipInputStream;
005: import java.net.URL;
006: import tide.exttools.ToolsAutoApplyManager;
007: import tide.project.*;
008: import tide.editor.*;
009: import javax.swing.*;
010: import java.awt.*;
011: import java.awt.event.*;
012: import javax.swing.border.*;
013: import java.util.*;
014: import java.io.*;
015: import snow.utils.*;
016: import snow.utils.storage.*;
017: import snow.utils.gui.*;
018:
019: public class PMDSettingsDialog extends JDialog {
020: private final FileField pmdPath = new FileField("", false,
021: "Give the PMD jar file location", JFileChooser.FILES_ONLY);
022: final private JButton viewOpts = new JButton("?");
023: private final JTextArea rulesArea = new JTextArea(7, 40);
024: public boolean dialogWasAccepted = false;
025:
026: public static final String defaultPMDLocation = "C:/Java/analysis_tools/pmd/pmd-4.1/lib/pmd-4.1.jar";
027: public static final String defaultOptions = "-targetjdk 1.6 -cpus 2 -minimumpriority 5";
028:
029: public static String recommandedPMDRules = "unusedcode,basic,imports";
030:
031: public final static String homepage = "http://pmd.sourceforge.net/";
032:
033: // 8MB
034: //public final static String directDownloadLink40 = "http://heanet.dl.sourceforge.net/sourceforge/pmd/pmd-bin-4.0.zip";
035: public final static String directDownloadLink41 = "http://switch.dl.sourceforge.net/sourceforge/pmd/pmd-bin-4.1.zip";
036:
037: // todo: link
038: public final static String rulesHomepage = "http://pmd.sourceforge.net/rules/index.html";
039:
040: public PMDSettingsDialog(final JFrame parent,
041: final ProjectSettings settings) {
042: super (parent, "PMD sourcecode checker", true);
043:
044: JComponent ta = GUIUtils
045: .createReadOnlyDescriptionArea("<html><body>PMD scans Java source code and looks for potential problems."
046: /*+"\n<ul><li> Empty try/catch/finally/switch blocks"
047: +"<li> Unused local variables, parameters and private methods"
048: +"<li> Empty if/while statements"
049: +"<li> Overcomplicated expressions - unnecessary if statements, for loops that could be while loops"
050: +"<li> Classes with high cyclomatic complexity measurements"
051: +"</ul>" */
052: + "<br>\n\nPlease visit "
053: + homepage
054: + " for the most up-to-date version and explanations."
055: + "<br><b>Manual Usage</b>: launch PMD from the context menu of the sources tree, for single files or branches."
056: + "</body>");
057: add(ta, BorderLayout.NORTH);
058:
059: JPanel inputPanel = new JPanel();
060: add(inputPanel, BorderLayout.CENTER);
061: GridLayout3 gl3 = new GridLayout3(2, inputPanel);
062:
063: JButton help = new JButton("Help", Icons.createHelpIcon(16,
064: true));
065: help.setMargin(new Insets(0, 2, 0, 2));
066: help.setFocusPainted(false);
067: gl3.add(help);
068:
069: help.addActionListener(new ActionListener() {
070: public void actionPerformed(ActionEvent ae) {
071: if (pmdPath.getPath() != null) {
072: try {
073: File local = new File(pmdPath.getPath()
074: .getParentFile().getParentFile(),
075: "docs/index.html");
076: if (local.exists()) {
077: SysUtils.openBrowser("file://" + local);
078: } else {
079: System.out.println("No local help: "
080: + local);
081: SysUtils.openBrowser(homepage);
082: }
083: } catch (Exception e) {
084: JOptionPane.showMessageDialog(null,
085: "Can't open browser.\nerr:"
086: + e.getMessage(), "Error",
087: JOptionPane.ERROR_MESSAGE);
088: }
089: }
090: }
091: });
092:
093: JButton down = new JButton("Download and install version 4.1",
094: Icons.sharedDownArrow);
095: down.setMargin(new Insets(0, 2, 0, 2));
096: down.setFocusPainted(false);
097: gl3.add(down);
098: //gl3.addSeparator();
099: down.addActionListener(new ActionListener() {
100: public void actionPerformed(ActionEvent ae) {
101: final long[] ds;
102: final URL url;
103: try {
104: url = new URL(directDownloadLink41);
105: ds = NetUtils.getDateAndSizeOnServer(url);
106: } catch (Exception ex) {
107: JOptionPane.showMessageDialog(null,
108: "Cannot download PMD (are you online?):\n "
109: + ex.getMessage(), "Error",
110: JOptionPane.ERROR_MESSAGE);
111: return;
112: }
113:
114: final JFileChooser fs = new JFileChooser();
115: fs
116: .setDialogTitle("Choose the folder to install PMD in");
117: fs.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
118: fs
119: .setApproveButtonText("Download and install PMD in this folder");
120: int rep = fs.showOpenDialog(PMDSettingsDialog.this );
121: if (rep != fs.APPROVE_OPTION)
122: return;
123:
124: final File destFolder = fs.getSelectedFile();
125:
126: final ProgressModalDialog pmd = new ProgressModalDialog(
127: PMDSettingsDialog.this , "Download PMD", true);
128: pmd.setProgressBounds(100);
129: pmd.start();
130: final File tempDest = new File(destFolder,
131: "pmd-bin-4.1.zip");
132: Thread t = new Thread() {
133: @Override
134: public void run() {
135: try {
136: pmd
137: .setProgressSection("Downloading PMD from sourceforge");
138: pmd
139: .setProgressComment("pmd-bin-4.1.zip (8MB)");
140: NetUtils.downloadFileFromServer(url,
141: tempDest, "pmd-bin-4.1.zip", pmd);
142:
143: // ok, => install
144: pmd
145: .setProgressSection("Unpacking PMD locally");
146: FileUtils.extractZipFile(tempDest,
147: destFolder, pmd);
148:
149: // link (pmd-4.1/lib/pmd-4.1.jar)
150: File pf = new File(destFolder,
151: "pmd-4.1/lib/pmd-4.1.jar");
152:
153: pmdPath.setPath(pf);
154: // todo: look if exist !!
155: } catch (Exception ex) {
156: JOptionPane.showMessageDialog(null,
157: "Cannot download PMD:\n "
158: + ex.getMessage(), "Error",
159: JOptionPane.ERROR_MESSAGE);
160: ex.printStackTrace();
161: } finally {
162: pmd.closeDialog();
163: }
164: }
165: };
166: t.start();
167:
168: //NetUtils.download()
169: }
170: });
171:
172: gl3.addSeparator();
173:
174: gl3.add("PMD application");
175: gl3.add(pmdPath);
176: pmdPath.setPath(settings.getProperty("PMD_path",
177: defaultPMDLocation));
178: if (MainEditorFrame.instance != null)
179: pmdPath.offerRememberedGlobalCompletion(
180: MainEditorFrame.instance.globalProperties,
181: "knownPMDs");
182: pmdPath.setComponentWidth(370);
183: pmdPath.setAutoColorized();
184: pmdPath.allowedExtensions.add("jar");
185: pmdPath.fileTypeDescription = "PMD executable jar";
186:
187: gl3.addTitleSeparator("Options");
188: gl3.add("Options");
189: JPanel argumentsPanel = new JPanel(new FlowLayout(
190: FlowLayout.LEFT, 0, 0));
191: viewOpts.setMargin(new Insets(0, 2, 0, 2));
192: viewOpts.setFocusPainted(false);
193: JTextField argsField = new JTextField(settings.getProperty(
194: "PMD_Options", defaultOptions), 40);
195: gl3.add(argumentsPanel);
196: argumentsPanel.add(argsField);
197: argumentsPanel.add(viewOpts);
198: viewOpts.addActionListener(new ActionListener() {
199: public void actionPerformed(ActionEvent ae) {
200: displayOptions();
201: }
202: });
203:
204: gl3.add("Rules to use");
205: gl3.add(new JScrollPane(rulesArea));
206: rulesArea.setText(settings.getProperty("PMD_UsedRules",
207: PMDSettingsDialog.recommandedPMDRules));
208: rulesArea.setLineWrap(true);
209:
210: if (rulesArea.getText().trim().length() == 0) {
211: rulesArea.setText(PMDSettingsDialog.recommandedPMDRules);
212: }
213: gl3.add("");
214: JPanel rulesPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,
215: 0, 0));
216: gl3.add(rulesPanel);
217: JButton uarBT = new JButton("Select rules to use");
218: uarBT.setMargin(new Insets(0, 2, 0, 2));
219: rulesPanel.add(uarBT);
220: uarBT.addActionListener(new ActionListener() {
221: public void actionPerformed(ActionEvent ae) {
222: viewAllAvailableRules();
223: }
224: });
225:
226: JButton defBT = new JButton("Use recommanded rules");
227: defBT.setMargin(new Insets(0, 2, 0, 2));
228: rulesPanel.add(Box.createHorizontalStrut(5));
229: rulesPanel.add(defBT);
230: defBT.addActionListener(new ActionListener() {
231: public void actionPerformed(ActionEvent ae) {
232: int rep = JOptionPane
233: .showConfirmDialog(
234: PMDSettingsDialog.this ,
235: "Are you sure? your actual settings will be lost.",
236: "Confirmation",
237: JOptionPane.YES_NO_CANCEL_OPTION);
238: if (rep == JOptionPane.YES_OPTION) {
239: rulesArea
240: .setText(PMDSettingsDialog.recommandedPMDRules);
241: }
242: }
243: });
244:
245: gl3.add("");
246: final JCheckBox autoApplyOnChanges = new JCheckBox(
247: "Auto apply on changed sources", settings
248: .getBooleanProperty("pmd_autoApplyOnChanges",
249: false));
250: gl3.add(autoApplyOnChanges);
251:
252: gl3.add("");
253: final JCheckBox autoRemIrrelevant = new JCheckBox(
254: "Auto remove messages declared as irrelevant", settings
255: .getBooleanProperty("pmd_ignoreIrrelevant",
256: false));
257: gl3.add(autoRemIrrelevant);
258:
259: gl3.addTitleSeparator("Other Apps");
260: gl3.add("");
261: JButton ddet = new JButton("PMD Cut and Paste detector");
262: ddet.setMargin(new Insets(0, 2, 0, 2));
263: gl3.add(ddet);
264: ddet.addActionListener(new ActionListener() {
265: public void actionPerformed(ActionEvent ae) {
266: startDuplicatesDetection();
267: }
268: });
269:
270: final CloseControlPanel ccp = new CloseControlPanel(this , true,
271: true, "Ok");
272: add(ccp, BorderLayout.SOUTH);
273:
274: pack();
275: setSize(600, 560);
276:
277: this .setLocationRelativeTo(parent);
278: this .setVisible(true); // MODAL => waits
279:
280: if (ccp.getWasCancelled())
281: return;
282:
283: dialogWasAccepted = true;
284:
285: // save settings
286: settings.setProperty("PMD_path", pmdPath.getPath()
287: .getAbsolutePath());
288: settings.setProperty("PMD_Options", argsField.getText());
289: settings.setProperty("PMD_UsedRules", formatRulesList(rulesArea
290: .getText()));
291: settings.setBooleanProperty("pmd_autoApplyOnChanges",
292: autoApplyOnChanges.isSelected());
293: settings.setBooleanProperty("pmd_ignoreIrrelevant",
294: autoRemIrrelevant.isSelected());
295:
296: //PMDLauncher.getInstance().configureAutoScan();
297: ToolsAutoApplyManager.getInstance();
298:
299: if (MainEditorFrame.instance != null) {
300: pmdPath.rememberPathForGlobalCompletion(
301: MainEditorFrame.instance.globalProperties,
302: "knownPMDs");
303: }
304:
305: } // Constructor
306:
307: private static String formatRulesList(final String raw) {
308: // remove spaces
309: //return raw.replaceAll("\\s+","");
310: return raw;
311: }
312:
313: /** @return true if PMD is present.
314: Used to detect if a setup dialog should be shown when starting the tool (if not found).
315: */
316: public static boolean isConfigured() {
317: ProjectSettings actualProject = MainEditorFrame.instance
318: .getActualProject();
319: return new File(actualProject.getProperty("PMD_path",
320: defaultPMDLocation)).exists();
321: }
322:
323: /** be careful, some of these rules requires some additional Jars to be present in the classpath !
324: JUnit, ...
325: */
326: private void viewAllAvailableRules() {
327: try {
328: File jf = pmdPath.getPath();
329: if (jf.exists()) {
330: JDialog d = new JDialog(this , "PMD rules selection",
331: true);
332:
333: RulesTree rt = new RulesTree();
334: d.add(rt, BorderLayout.CENTER);
335:
336: rt.setAvailableRules(PMDLauncher.readRulesetsNames(jf,
337: true));
338: rt.setSelectedRules(rulesArea.getText());
339:
340: CloseControlPanel ccp = new CloseControlPanel(d, true,
341: true, "Ok");
342: d.add(ccp, BorderLayout.SOUTH);
343: d.setSize(900, 760);
344: d.setLocationRelativeTo(this );
345: d.setVisible(true); // modal
346:
347: if (ccp.getWasCancelled() || !ccp.getWasAccepted())
348: return;
349:
350: // save
351: rulesArea.setText(rt.getSelectedRules());
352:
353: }
354: } catch (Exception e) {
355: JOptionPane.showMessageDialog(this , "" + e.getMessage(),
356: "Error", JOptionPane.ERROR_MESSAGE);
357: e.printStackTrace();
358: }
359: }
360:
361: /** PMD also contains a code duplicates detection programm.
362: */
363: private void startDuplicatesDetection() {
364: ProjectSettings actualProject = MainEditorFrame.instance
365: .getActualProject();
366: StringBuilder comp = new StringBuilder();
367: try {
368: Vector<String> args = new Vector<String>();
369: args.add(actualProject.getJava_TOOL().getAbsolutePath());
370: args.add("-Xmx1024m"); // maybe more ?
371: args.add("-cp");
372: args.add(pmdPath.getPath().getAbsolutePath());
373: args.add("net.sourceforge.pmd.cpd.GUI");
374:
375: ProcessUtils.executeProcessAndGobble(args,
376: "Duplicates detection");
377: } catch (Exception e) {
378: comp.append("\r\nError: " + e.getMessage());
379: e.printStackTrace();
380: }
381: }
382:
383: /** error: prints out the args as args of a throwed runtime exception !
384: * TODO: filter the stacktrace out of the text.
385: */
386: private void displayOptions() {
387: String javaExe = "";
388: if (MainEditorFrame.instance != null) {
389: ProjectSettings actualProject = MainEditorFrame.instance
390: .getActualProject();
391: javaExe = actualProject.getJava_TOOL().getAbsolutePath();
392: } else {
393: javaExe = new File(System.getProperty("java.home"),
394: "bin/java.exe").getAbsolutePath();
395: }
396: StringBuilder comp = new StringBuilder();
397: try {
398: Vector<String> args = new Vector<String>();
399: args.add(javaExe);
400: args.add("-jar");
401: args.add(pmdPath.getPath().getAbsolutePath());
402: args.add("-help");
403:
404: String help = ProcessUtils.readWholeProcessStack(args);
405:
406: // filter the buggy output ! ( also 4.0)
407:
408: StringBuilder nh = new StringBuilder();
409: BufferedReader br = new BufferedReader(new StringReader(
410: help));
411: String l = null;
412: while ((l = br.readLine()) != null) {
413: if (l.startsWith("Exception in thread "))
414: continue;
415: if (l.trim().startsWith("at net.sourceforge.pmd."))
416: continue;
417:
418: nh.append(l + "\r\n");
419: }
420:
421: help = nh.toString().trim();
422:
423: // bug, shows an exception...
424: comp.append(help);
425: //? comp.append("\r\nYou can also pass the option -J-Xmx256m to increase the memory for large projects doc creation");
426: } catch (Exception e) {
427: comp.append("\r\nError: " + e.getMessage());
428: e.printStackTrace();
429: }
430:
431: JDialog optionsDialog = new JDialog(this ,
432: "Available PMD options", false);
433: JTextPane tp = new JTextPane();
434: tp.setFont(MainEditorFrame.fixedWidthFontForProcesses);
435: tp.setText(comp.toString().trim());
436: optionsDialog.add(new JScrollPane(tp), BorderLayout.CENTER);
437: CloseControlPanel ccp = new CloseControlPanel(optionsDialog,
438: false, true, "Close");
439: optionsDialog.add(ccp, BorderLayout.SOUTH);
440: tp.setEditable(false);
441: tp.setCaretPosition(0);
442: optionsDialog.setSize(800, 550);
443: optionsDialog.setLocationRelativeTo(viewOpts);
444: optionsDialog.setVisible(true);
445: }
446: /*test
447: public static void main(String[] aa)
448: {
449: JFrame f = new JFrame("Test");
450: f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
451: ProjectSettings props = new ProjectSettings();
452: new PMDSettingsDialog(f, props);
453: System.exit(0);
454: }*/
455: }
|