001: package tide.exttools.findbugs;
002:
003: import java.text.SimpleDateFormat;
004: import tide.execute.ProcessesManager;
005: import snow.utils.ProcessUtils;
006: import tide.sources.SourceFile;
007: import tide.editor.*;
008: import tide.project.*;
009: import tide.utils.*;
010: import tide.editor.linemessages.*;
011: import snow.utils.gui.*;
012: import snow.utils.storage.*;
013: import javax.swing.JFrame;
014: import java.io.*;
015: import java.util.*;
016:
017: /** Calls the external findbugs to build a bug db and print te output and add line messages.
018: */
019: public class FindBugsLauncher {
020: // arguments
021: private final String javaExeLocation;
022: private final File findBugsHome;
023: private final File findBugsJar;
024: private final String classesLocation;
025: private final File bugFileLocation;
026: private final String auxClassPath;
027: private final String onlyAnalyse;
028: private final String options;
029: private final SourceFile toAnalyse; // file or folder (null => all)
030:
031: private Process process = null;
032: private boolean aborted = false;
033:
034: /**
035: * @param onlyAnalyse com/foobar/MyClass,com.foobar.mypkg.- // only correct and working since i've transmitted the bug to fb team.
036: */
037: public FindBugsLauncher(
038: SourceFile toAnalyse, // if null => analyse all
039: String onlyAnalyse, // if null of empty => analyse all
040: int nClasses, final File bugsOutputFile,
041: JFrame parentFrame, AppProperties iniFile,
042: EstimatedProgressDialog progressDialog) throws Exception {
043: this .toAnalyse = toAnalyse;
044: ProjectSettings actualProject = MainEditorFrame.instance
045: .getActualProject();
046:
047: if (onlyAnalyse == null) {
048: // remove all
049: LineMessagesManager.getInstance().removeMessages(
050: FBLineMessage.class);
051: } else {
052: // remove only for analysed (recursive)
053: LineMessagesManager
054: .getInstance()
055: .removeMessagesForJavaNameStartingWith(
056: onlyAnalyse.substring(0, onlyAnalyse
057: .length() - 2), FBLineMessage.class);
058: }
059:
060: findBugsHome = new File(actualProject.getProperty(
061: "FindBugs_path",
062: FindBugsSettingsDialog.defaultFindBugsLocation));
063: File javaExePath = actualProject.getJava_TOOL();
064: File classesRoot = actualProject.getClasses_Home();
065: String auxClassPath = FileUtils.filesToList(actualProject
066: .getClassPath(true, true)); // user cp [March2007] + JRE jars !
067: options = actualProject.getProperty("FindBugs_Options", "")
068: .trim();
069: boolean ignoreIrrelevant = actualProject.getBooleanProperty(
070: "FindBugs_ignoreIrrelevant", false);
071:
072: // test arguments
073: if (!javaExePath.exists())
074: throw new Exception("Java exe path "
075: + javaExePath.getAbsolutePath() + " doesn't exist");
076: if (!findBugsHome.exists())
077: throw new Exception("Findbugs path "
078: + findBugsHome.getAbsolutePath() + " doesn't exist");
079: if (!classesRoot.exists())
080: throw new Exception("Class root "
081: + classesRoot.getAbsolutePath() + " doesn't exist");
082: if (bugsOutputFile == null)
083: throw new Exception("null output file");
084:
085: this .javaExeLocation = javaExePath.getAbsolutePath();
086: this .findBugsJar = new File(findBugsHome, "lib/findbugs.jar");
087: this .classesLocation = classesRoot.getAbsolutePath();
088: this .bugFileLocation = bugsOutputFile;
089: this .auxClassPath = auxClassPath;
090: this .onlyAnalyse = onlyAnalyse;
091:
092: if (bugsOutputFile.getParentFile() != null
093: && !bugsOutputFile.getParentFile().exists()) {
094: bugsOutputFile.getParentFile().mkdirs();
095: }
096:
097: progressDialog.setCommentLabel("Preparation...");
098: double timePerClass = iniFile.getDouble(
099: "FindBugsProcess_time_per_class_level", 200); //+detection_level, 200); // ms
100: progressDialog.setIndeterminate((long) timePerClass * nClasses,
101: true);
102:
103: try {
104: progressDialog.setCommentLabel("Analysing " + nClasses
105: + " classes...");
106: long start = System.currentTimeMillis();
107:
108: generateBugsDB(progressDialog);
109:
110: long time = System.currentTimeMillis() - start;
111: double tpc = (double) time / nClasses;
112: iniFile.setDouble("FindBugsProcess_time_per_class_level",
113: tpc);
114: } catch (Exception e) {
115: throw e;
116: } finally {
117: progressDialog.closeDialog();
118: }
119:
120: FindBugsReader fbr = new FindBugsReader(bugsOutputFile);
121: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
122: .appendLine("\r\n" + fbr.toStringStatistics()); // toString() => full bugs (debug) !..., toStringStatistics: stat
123:
124: List<BugInstance> bugs = fbr.getBugs();
125: if (bugs.size() > 0) {
126: int numberOfMessagesAdded = 0;
127: int numberOfMessagesIgnored_irrelevant = 0;
128: int numberOfMessagesIgnored_onlyAnalyse = 0;
129:
130: for (final BugInstance bin : bugs) {
131: // maybe several lines per bug ! in several sources. (as for sync bug)
132: int[] stats = bin.createAndAddLineMessage(
133: ignoreIrrelevant, onlyAnalyse);
134: numberOfMessagesAdded += stats[0];
135: numberOfMessagesIgnored_irrelevant += stats[1];
136: numberOfMessagesIgnored_onlyAnalyse += stats[2];
137: }
138:
139: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
140: .appendLine("");
141:
142: if (numberOfMessagesIgnored_irrelevant > 0) {
143: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
144: .appendLine(""
145: + numberOfMessagesIgnored_irrelevant
146: + " bugs marked as irrelevant were ignored.");
147: }
148:
149: if (numberOfMessagesIgnored_onlyAnalyse > 0) {
150: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
151: .appendLine(""
152: + numberOfMessagesIgnored_onlyAnalyse
153: + " bugs were ignored (because not in "
154: + onlyAnalyse + ").");
155: }
156:
157: if (numberOfMessagesAdded > 0) {
158: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
159: .appendLine("\r\nThe bugs are in the messages tab ("
160: + numberOfMessagesAdded + " messages).");
161: } else {
162: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
163: .appendLine("\r\nNo bugs were added.");
164: }
165: }
166:
167: // always
168: LineMessagesManager.getInstance().refreshView();
169:
170: // XSLT NOT DONE YET...
171: /*String url = "tide/exttools/findbugs/simple_findbugs_report.xsl";
172: InputStream is = FindBugsLauncher.class.getClassLoader().getResourceAsStream(url); // jar mode
173: if(is==null)
174: {
175: // IDE mode (develop)
176: File fi = new File(url);
177: if(fi.exists())
178: {
179: is = new FileInputStream( fi );
180: }
181: else
182: {
183: System.out.println("File not found: "+fi);
184: }
185: }*/
186: /*
187: if(is!=null)
188: {
189: try
190: {
191: String res = XMLUtils.transformXML( is, new StreamSource(file));
192: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.append(res);
193: }
194: catch(Exception e)
195: {
196: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.appendError("ERROR during PMD XSLT transform: "+e.getMessage()+"\n");
197: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.append(FileUtils.getFileStringContent(file));
198: }
199: finally
200: {
201: is.close();
202: }
203: }
204: else
205: {
206: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.append(FileUtils.getFileStringContent(file));
207: }
208: */
209:
210: } // Constructor
211:
212: /** fb 1.3 has no more support for -onlyAnalyze !
213: */
214: private final boolean useOldModePrior13 = true;
215:
216: /**
217: */
218: private final void generateBugsDB(ProgressModalDialog progressDialog)
219: throws Exception {
220: final ProjectSettings projectSettings = MainEditorFrame.instance
221: .getActualProject();
222: List<String> args = new ArrayList<String>();
223:
224: args.add(javaExeLocation);
225: args.add("-Xmx512m"); // TODO: options...
226:
227: // required since 1.1.2 !
228: args.add("-Dfindbugs.home=" + findBugsHome.getAbsolutePath());
229:
230: String lang = projectSettings.getProperty(
231: "FindBugs_messagesLanguage", "en");
232: //System.getProperty("user.language","en"));
233: args.add("-Duser.language=" + lang); // SHOULD BE Fix for message categorization
234:
235: //args.add("-Dfindbugs.debug=true");
236:
237: args.add("-jar");
238: args.add(findBugsJar.getAbsolutePath());
239:
240: // if(findBugsJar.getAbsolutePath().contains("1.3")) useOldModePrior13 = false;
241:
242: if (options.length() > 0) {
243: args.addAll(ProjectUtils.splitArgs(options, false));
244: }
245:
246: /* args.add("-home");
247: args.add(findBugsHomeLocation); */
248:
249: // args.add("-"+this.detection_level);
250: args.add("-nested:false");
251:
252: if (bugFileLocation != null) {
253: args.add("-outputFile");
254: args.add(bugFileLocation.getAbsolutePath());
255: }
256:
257: //args.add("-projectName");
258: //args.add("tide["+projectSettings.getProjectName()+"]");
259: /*
260: args.add("-release");
261: args.add("R"+SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT).format(System.currentTimeMillis()));
262: */
263:
264: /*else
265: {
266: // will be printed on stdout !
267: }*/
268:
269: //args.add("-sortByClass");
270: // no more supported in 1.3.0 !! :-(
271: //
272: if (onlyAnalyse != null && onlyAnalyse.length() > 0) // Doesn't work anymore on findbugs 1.3.0
273: {
274: args.add("-onlyAnalyze");
275: args.add(onlyAnalyse);
276: }
277:
278: if (auxClassPath != null && auxClassPath.length() > 0) {
279: args.add("-auxclasspath");
280: String cp = auxClassPath.replaceAll(";\\s*", ";");
281: args.add(cp);
282: }
283:
284: // some detectors use the source, furthermore, it allows for browsing with the findbugsGUI
285: args.add("-sourcepath");
286: args.add(projectSettings.getSources_Home().getAbsolutePath());
287:
288: //args.add("-omitVisitors");
289: //args.add("MS,EI_EXPOSE_REP2");
290:
291: args.add("-xml:withMessages");
292:
293: //NOargs.add(classesLocation);
294:
295: /*
296: if(toAnalyse==null)
297: {
298: // analyse all => pass the class folder
299: if(toAnalyse==null)
300: {
301: args.add(classesLocation);
302: } // otherwise added below
303: }
304: else
305: {
306: args.add("-xargs");
307: // added below
308: } */
309:
310: if (useOldModePrior13) {
311: args.add(classesLocation);
312: } else {
313: if (toAnalyse != null) {
314: // all project files wil be added below !
315: args.add("-xargs");
316: } else {
317: // Will analyse ALL
318: args.add(classesLocation);
319: }
320: }
321:
322: MainEditorFrame.debugOut("Running findbugs " + args);
323:
324: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
325: .setText("FindBugs Analysis:\r\n");
326: MainEditorFrame.instance.outputPanels.selectToolsTab(false);
327: if (options.length() > 0) {
328: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
329: .append(" Options: " + options + "\r\n");
330: }
331: if (onlyAnalyse != null && onlyAnalyse.length() > 0) {
332: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
333: .append(" Only analysed: " + onlyAnalyse + "\r\n");
334: }
335:
336: try {
337: ProcessBuilder pb = new ProcessBuilder(args);
338: process = pb.start();
339: if (progressDialog != null) {
340: progressDialog.setProcessToOptionalKill(process);
341: }
342: MainEditorFrame.instance.outputPanels.processesManager
343: .addProcess("FindBugs analysis", process, true);
344: } catch (Exception e) {
345: throw new Exception("Cannot start findbugs: "
346: + e.getMessage(), e);
347: }
348:
349: // Gobbling error stream
350: final InputStream es = process.getErrorStream();
351:
352: final StringBuilder error = new StringBuilder();
353: Thread reader = new Thread() {
354: public void run() {
355: //System.out.println("Start reading");
356: byte[] buf = new byte[128];
357: int read;
358: try {
359: while ((read = es.read(buf)) != -1) {
360: String err = new String(buf, 0, read);
361: //System.out.println("ERR>"+err);
362: error.append(err);
363: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
364: .appendError(err);
365: }
366: } catch (Exception ee) {
367: ee.printStackTrace();
368: }
369: }
370: };
371: reader.start();
372:
373: // Gobbling input stream
374: final InputStream is = process.getInputStream();
375: Thread readerIS = new Thread() {
376: public void run() {
377: //System.out.println("Start reading");
378: byte[] buf = new byte[128];
379: int read;
380: try {
381: while ((read = is.read(buf)) != -1) {
382: // some warnings ??
383: // not important, the content is in the file
384: System.out.println(new String(buf, 0, read));
385: }
386: } catch (Exception ee) {
387: ee.printStackTrace();
388: }
389: }
390: };
391: readerIS.start();
392:
393: if (!useOldModePrior13 && toAnalyse != null)
394: //TODO: also add the dependencies !!! (don't work, needs recursive deps check => pass all.)
395: {
396: final OutputStream pos = process.getOutputStream();
397: PrintWriter pw = new PrintWriter(pos);
398: /*
399: // good time to analyse now...
400: //
401: Set<SourceFile> allFilesToAnalyse = new HashSet<SourceFile>();
402:
403: if(toAnalyse.isDirectory())
404: {
405: List<SourceFile> files = new ArrayList<SourceFile>();
406: MainEditorFrame.instance.sourcesTreePanel.getTreeModel().
407: getAllSourceFilesRecurse(toAnalyse, files, false, false);
408: for(SourceFile sfi : files)
409: {
410: allFilesToAnalyse.add(sfi);
411: allFilesToAnalyse.addAll( sfi.sourceFileDependencies.classesUsedBy );
412: }
413: }
414: else
415: {
416: allFilesToAnalyse.add(toAnalyse);
417: allFilesToAnalyse.addAll( toAnalyse.sourceFileDependencies.classesUsedBy );
418: }
419:
420: MainEditorFrame.debugOut(""+allFilesToAnalyse.size()+" sources (with deps) to pass to FB");
421: for(SourceFile sfi : allFilesToAnalyse)
422: {
423: for(File fi : sfi.lookForAssociatedClassFiles())
424: {
425: pw.println(fi.getAbsolutePath()); // flushes
426: }
427: }
428: pw.close();*/
429:
430: List<File> allClassFiles = new ArrayList<File>();
431: FileUtils.getAllClassFilesRecurse(projectSettings
432: .getClasses_Home(), allClassFiles, true);
433: System.out.println("passing " + allClassFiles.size()
434: + " classes");
435: for (File fi : allClassFiles) {
436: pw.println(fi.getAbsolutePath()); // flushes
437: }
438:
439: pw.close();
440:
441: }
442:
443: // Waits for completion (or abort)
444: //
445: MainEditorFrame.debugOut("wait for fb to complete...");
446: int code = process.waitFor();
447: //MainEditorFrame.debugOut("FindBugs process terminated");
448: process = null;
449:
450: if (aborted) {
451: throw new Exception("FindBugs process aborted");
452: }
453:
454: if (code != 0) {
455: if (progressDialog.getWasCancelled()) {
456: throw new Exception("FindBugs process was cancelled");
457: } else {
458: throw new Exception(
459: "Error occured during findbugs process\n"
460: + error.toString());
461: }
462: }
463:
464: if (error.length() > 0) {
465: String errMess = error.toString();
466: MainEditorFrame
467: .debugOut("FB Warning: following appears on stderr:\n"
468: + errMess);
469: // shit, the process also maps normal output to system.err
470: // we make the hypothesis that there were no errors...
471: // we will at least see if the bug file exists or not...
472: }
473:
474: if (toAnalyse == null) {
475: try {
476: // whole analysis => store the global analysis in the project
477: FileUtils.copy(bugFileLocation, new File(
478: projectSettings.getProjectSettingsFolder(),
479: "latest_findbugs_analysis.xml"));
480: } catch (Exception e) {
481: e.printStackTrace();
482: }
483: }
484:
485: /* Debug
486: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.append(
487: "\r\n"+
488: FileUtils.getFileStringContent( bugFileLocation )
489: );
490: */
491: }
492:
493: /**
494: */
495: public static final void launchFindBugsGUI(final File projFile)
496: throws Exception {
497: final ProjectSettings actualProject = MainEditorFrame.instance
498: .getActualProject();
499: File findBugsHome = new File(actualProject.getProperty(
500: "FindBugs_path",
501: FindBugsSettingsDialog.defaultFindBugsLocation));
502: File findBugsJarLocation = new File(findBugsHome,
503: "lib/findbugsGUI.jar");
504:
505: List<String> args = new ArrayList<String>();
506:
507: args.add(actualProject.getJava_TOOL().getAbsolutePath());
508: args.add("-Xmx512m"); // TODO: options...
509:
510: // required since 1.1.2 !
511: args.add("-Dfindbugs.home=" + findBugsHome.getAbsolutePath());
512: //args.add("-Dfindbugs.classscreener.debug=true");
513:
514: String lang = actualProject.getProperty(
515: "FindBugs_messagesLanguage", "en");
516: //System.getProperty("user.language","en"));
517: args.add("-Duser.language=" + lang); // SHOULD BE Fix for message categorization
518:
519: //args.add("-Dfindbugs.debug=true");
520:
521: args.add("-jar");
522: args.add(findBugsJarLocation.getAbsolutePath());
523: //args.add("-look:plastic");
524:
525: //args.add("-gui"); //old: "gui"
526:
527: //args.add("-sourcepath");
528: //args.add(actualProject.getSources_Home().getAbsolutePath());
529:
530: /*
531: if(projFile!=null)
532: {
533: //args.add("-project");
534: args.add(projFile.getAbsolutePath());
535: }*/
536:
537: //args.add(projFile.getAbsolutePath());
538:
539: /*File classesRoot = actualProject.getClasses_Home();
540: args.add(classesRoot.getAbsolutePath());*/
541:
542: //System.out.println("Launching Findbugs GUI: "+args);
543: Process p = ProcessUtils.executeProcessAndGobbleToConsole(
544: "findbugsGUI", actualProject.getSources_Home(), // ??
545: args.toArray(new String[args.size()]));
546:
547: //ProcessesManager.;
548:
549: }
550:
551: /*test
552: public static void main(Strin g argv[])
553: {
554: System.out.println("aaa.b".replace('.', '\\'));
555:
556: if(true) return;
557:
558: try
559: {
560: AppProperties ap = new AppProperties();
561: FindBugsLauncher fps = new FindBugsLauncher(
562: "*",
563: 111,
564: //new File("c:/java/jdk1.6.0_05/bin/java.exe"),
565: //new File("c:/findbugs/findbugs-1.2.0"),
566: //new File("C:/classes/ErrorTest"),
567: //"",
568: new File("c:/findbugs/test.bugs.xml"),
569: // FindBugsLauncher.DETECTION_LEVEL_MEDIUM,
570: new JFrame(), ap
571: );
572:
573: }
574: catch(Exception e)
575: {
576: e.printStackTrace();
577: }
578: }*/
579:
580: }
|