001: package tide.exttools.JLint;
002:
003: import snow.utils.gui.*;
004: import snow.utils.*;
005: import tide.editor.MainEditorFrame;
006: import tide.project.*;
007: import tide.sources.*;
008: import tide.editor.linemessages.*;
009: import java.util.*;
010: import java.io.*;
011: import javax.swing.*;
012: import java.util.concurrent.*;
013:
014: public final class JLintChecker {
015: private JLintChecker() {
016: }
017:
018: /** This creates a task and performs the analysis.
019: */
020: public static void analyse(final List<SourceFile> sources) {
021: // TODO: remove only for the analysed classes
022: LineMessagesManager.getInstance().removeMessages(
023: JLintLineMessage.class);
024:
025: MainEditorFrame.instance.outputPanels.selectToolsTab(false);
026: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
027: .clearDocument();
028:
029: final ProgressModalDialog pmd = new ProgressModalDialog(
030: MainEditorFrame.instance,
031: "JLint static checker analysis", false);
032: Runnable task = new Runnable() {
033: public void run() {
034: pmd.start();
035: try {
036: List<File> classes = new ArrayList<File>();
037: for (SourceFile sf : sources) {
038: List<File> acf = sf
039: .lookForAssociatedClassFiles();
040: if (acf != null) {
041: classes.addAll(acf); // TODO: BETTER !
042: }
043: //System.out.println("Class files for "+sf.javaName+": "+sf.classFiles);
044: }
045:
046: pmd.setProgressBounds(classes.size());
047:
048: ProjectSettings settings = MainEditorFrame.instance
049: .getActualProject();
050: String options = settings.getProperty(
051: "JLint_Options", "");
052: boolean ignoreIrrelevant = settings
053: .getBooleanProperty(
054: "JLint_ignoreIrrelevant", false);
055: File jlintExe = new File(settings.getProperty(
056: "JLint_path",
057: JLintSettingsDialog.defaultJLintLocation));
058: File sourcesPath = settings.getSources_Home();
059:
060: List<String> ignoredMessageParts = JLintSettingsDialog
061: .getLines(settings.getProperty(
062: "JLint_ignoredSubstrings", ""));
063:
064: // avoid messages appearing twice !
065: File historyFile = File.createTempFile(
066: "JLintHistory", ".temp");
067: historyFile.deleteOnExit();
068:
069: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
070: .appendLine("JLint analysis of "
071: + classes.size() + " classes:\r\n");
072: if (options.length() > 0) {
073: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
074: .appendLine("Options: " + options);
075: }
076: if (ignoredMessageParts != null
077: && ignoredMessageParts.size() > 0) {
078: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
079: .appendLine("Ignores messages containing");
080: for (String mi : ignoredMessageParts) {
081: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
082: .appendLine(" " + mi);
083: }
084: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
085: .appendLine("");
086: }
087:
088: pmd.setCommentLabel("Analysing " + classes.size()
089: + " classes...");
090:
091: int ignoredCount = 0;
092: int wroteCount = 0;
093: for (File cf : classes) {
094: int[] count = analyseClassFile(jlintExe,
095: historyFile, cf, sourcesPath, options,
096: ignoredMessageParts, ignoreIrrelevant,
097: pmd);
098: ignoredCount += count[0];
099: wroteCount += count[1];
100: }
101: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
102: .appendLine("\r\nJLint analysis done, "
103: + wroteCount + " messages, "
104: + ignoredCount + " ignored."
105: + " (see Messages tab)");
106: } catch (Exception e) {
107: JOptionPane.showMessageDialog(pmd, "Error: "
108: + e.getMessage());
109: e.printStackTrace();
110: } finally {
111: pmd.closeDialog();
112: LineMessagesManager.getInstance().refreshView();
113: }
114: }
115: };
116:
117: Future fut = MainEditorFrame.instance.executeTask(task);
118: MainEditorFrame.instance.outputPanels.processesManager
119: .addProcess("JLint analysis", fut, true);
120:
121: }
122:
123: /** Analyse a single class file.
124: * @param historyFile useful to delete duplicates. Should be deleted at the beginning of a new analysis !
125: * @return {the number of ignored lines, number of wrote lines}
126: */
127: private static int[] analyseClassFile(File jlintExe,
128: File historyFile, File classFile, File sourceBase,
129: String options, List<String> ignoredMessageParts,
130: boolean ignoreIrrelevant, ProgressModalDialog pmd)
131: throws Exception {
132: if (!jlintExe.exists()) {
133: throw new FileNotFoundException("JLint exe not found in "
134: + jlintExe.getAbsolutePath());
135: }
136:
137: List<String> execCommand = new ArrayList<String>();
138: execCommand.add(jlintExe.getAbsolutePath());
139: // options
140: if (options != null && options.trim().length() > 0) {
141: execCommand.addAll(ProjectUtils.splitArgs(options, false));
142: }
143: execCommand.add("-history");
144: execCommand.add(historyFile.getAbsolutePath());
145:
146: execCommand.add("-done"); // avoid writing of a done message for each analysed class
147:
148: execCommand.add("-source");
149: execCommand.add(sourceBase.getAbsolutePath());
150:
151: // the clas file to analyse
152: execCommand.add(classFile.getAbsolutePath());
153:
154: //
155: Process proc = Runtime.getRuntime().exec(
156: execCommand.toArray(new String[execCommand.size()]));
157:
158: if (pmd != null) {
159: pmd.setProcessToOptionalKill(proc);
160: pmd.incrementProgress(1);
161: }
162: //MainEditorFrame.instance.outputPanels.processesManager.addProcess("JLint analysis", proc); // no, would be one per class (too much !)
163:
164: // TODO: filter each lines !
165: JLintStreamGobbler sg = new JLintStreamGobbler(proc
166: .getInputStream(),
167: //MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.createWriterForThisDocument(false),
168: ignoredMessageParts, ignoreIrrelevant);
169: sg.start();
170:
171: // errors
172: StreamGobbler sge = new StreamGobbler(
173: proc.getErrorStream(),
174: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
175: .createWriterForThisDocument(true), "");
176: sge.start();
177:
178: proc.waitFor();
179:
180: sg.join(); // wait until wrote.
181: sge.join();
182:
183: return new int[] { sg.ignoredCount, sg.wroteLinesCount };
184: }
185:
186: /** Readss the input lines and ignore some containing specified text.
187: */
188: static final class JLintStreamGobbler extends Thread {
189: private final InputStream inputStream;
190: List<String> ignoredMessageParts;
191: public int ignoredCount = 0;
192: public int wroteLinesCount = 0;
193: final boolean ignoreIrrelevant;
194:
195: public JLintStreamGobbler(final InputStream inputStream,
196: List<String> ignoredMessageParts,
197: final boolean ignoreIrrelevant) {
198: this .inputStream = inputStream;
199: this .ignoredMessageParts = ignoredMessageParts;
200: this .ignoreIrrelevant = ignoreIrrelevant;
201: }
202:
203: @Override
204: public void run() {
205: try {
206: final InputStreamReader isr = new InputStreamReader(
207: this .inputStream);
208: final BufferedReader br = new BufferedReader(isr);
209:
210: String line;
211: wl: while ((line = br.readLine()) != null) {
212: if (shouldIgnoreQ(line)) {
213: ignoredCount++;
214: continue wl;
215: } else {
216: wroteLinesCount++;
217: }
218:
219: JLintLineMessage.createAndAdd(line,
220: ignoreIrrelevant);
221:
222: }
223: } catch (IOException ioe) {
224: ioe.printStackTrace();
225: }
226: }
227:
228: public boolean shouldIgnoreQ(String line) {
229: // some lines like: "java\lang\String.java:1: equals() was overridden but not hashCode()."
230: // they are clearly outside of the analysed scope
231: // AND IF WE ANALYSE java.* with this tool ???
232: // TODO: look if this is the case, otherwise, ignore these lines.
233: //if(line.startsWith("java\\") return true;
234:
235: if (ignoredMessageParts == null)
236: return false;
237:
238: for (String imp : ignoredMessageParts) {
239: if (line.indexOf(imp) >= 0)
240: return true;
241: }
242: return false;
243:
244: }
245:
246: }
247:
248: }
|