001: package tide.editor.linemessages;
002:
003: import tide.staticanalysis.TintLineMessage;
004: import tide.editor.debugger.BreakpointLineMessage;
005: import java.util.*;
006: import tide.editor.bookmarks.SourceBookmark;
007: import tide.javadocgen.JavaDocLineMessage;
008: import tide.editor.MainEditorFrame;
009: import tide.editor.ProjectListener;
010: import snow.utils.storage.*;
011: import tide.project.ProjectSettings;
012: import java.io.*;
013: import java.awt.EventQueue;
014:
015: import tide.exttools.findbugs.FBLineMessage;
016: import tide.exttools.checkstyle.CSLineMessage; //import tide.syntaxtree.SSTLineMessage;
017: import tide.exttools.lint4j.Lint4JLineMessage;
018: import tide.exttools.JLint.JLintLineMessage;
019: import tide.exttools.PMD.PMDMessage;
020:
021: /** Contains all line messages.
022: * maps line messages with files
023: * TODO: detect messages corresponding to no source (checkstyle generate messages for resources too...)
024: *
025: * Also shifts bookmarks ! and columns numbers
026: */
027: public final class LineMessagesManager {
028: // singleton
029: private static LineMessagesManager instance = null;
030:
031: // the messages, stored per file (key=javaName)
032: final Map<String, ArrayList<LineMessage>> messagesForFile = new HashMap<String, ArrayList<LineMessage>>();
033:
034: final HashSet<String> relevantCategories = new HashSet<String>();
035: final HashSet<String> irrelevantCategories = new HashSet<String>();
036:
037: public HashSet<String> getRelevantCategories() {
038: return relevantCategories;
039: }
040:
041: public HashSet<String> getIrrelevantCategories() {
042: return irrelevantCategories;
043: }
044:
045: private LineMessagesManager() {
046: MainEditorFrame.instance
047: .addProjectListener(new ProjectListener() {
048: // public void projectWillBeLoaded() {}
049: public void projectHasBeenLoaded(
050: final ProjectSettings ps) {
051: Thread t = new Thread() {
052: public void run() {
053: try {
054: restoreFromFile(ps);
055: } catch (Exception e) {
056: e.printStackTrace();
057: }
058: }
059: };
060: t.setName("restoring line messages");
061: t.start();
062: }
063:
064: public void projectIsSaving(ProjectSettings ps) {
065: }
066:
067: // public void projectWillClose(ProjectSettings ps) {}
068: public void projectHasBeenClosed(ProjectSettings ps) {
069: // don't wait, do it now
070: try {
071: storeToFile(ps);
072: } catch (Exception e) {
073: e.printStackTrace();
074: }
075: }
076: });
077: }
078:
079: public static synchronized LineMessagesManager getInstance() {
080: if (instance == null) {
081: instance = new LineMessagesManager();
082: }
083: return instance;
084: }
085:
086: /** the view must be manually refreshed !
087: */
088: public static void add(LineMessage mess) {
089: synchronized (LineMessagesManager.getInstance()) {
090: LineMessagesManager.getInstance().add_(
091: mess.getSourceJavaName(), mess);
092: }
093: }
094:
095: /** not synchronized
096: */
097: private void addNC(LineMessage mess) {
098: add_(mess.getSourceJavaName(), mess);
099: }
100:
101: private void add_(String javaName, LineMessage mess) {
102: if (!messagesForFile.containsKey(javaName))
103: messagesForFile.put(javaName, new ArrayList<LineMessage>());
104: messagesForFile.get(javaName).add(mess);
105: }
106:
107: /** Should be called at the end of a process (compilation, check), ...
108: */
109: public void refreshView() {
110: // refresh the ruler
111: MainEditorFrame.instance.editorPanel.getLinePanel()
112: .refreshMessages();
113:
114: // refresh the overview table
115:
116: EventQueue.invokeLater(new Runnable() {
117: public void run() {
118:
119: MessagesTable.getInstance().refresh();
120: }
121: });
122:
123: }
124:
125: /** Should be called before a compilation, analysis, ...
126: * @return the number of removed bugs.
127: */
128: public int removeMessages(Class type) {
129: List<LineMessage> toRemove = new ArrayList<LineMessage>();
130: for (ArrayList<LineMessage> lms : messagesForFile.values()) {
131: toRemove.clear();
132: for (LineMessage lm : lms) {
133: if (lm.getClass() == type) {
134: toRemove.add(lm);
135: }
136: }
137: lms.removeAll(toRemove);
138: }
139:
140: return toRemove.size();
141: }
142:
143: /** To call when the source is removed
144: */
145: public void removeAllMessagesFor(final String javaName) {
146: messagesForFile.remove(javaName);
147: }
148:
149: /**
150: */
151: public void removeAllMessages() {
152: messagesForFile.clear();
153: }
154:
155: /** Manually called from the options menu.
156: * @return the number of removed bugs.
157: */
158: public int removeIrrelevantMessages() {
159: int rem = 0;
160: for (String jn : messagesForFile.keySet()) {
161: ArrayList<LineMessage> messs = messagesForFile.get(jn);
162: if (messs == null)
163: continue; // [Nov2007]: Was an error, not found by a tool...
164: // (was "return") found when defining a non void return value !
165:
166: List<LineMessage> toRemove = new ArrayList<LineMessage>();
167: for (LineMessage lm : messs) {
168: if (irrelevantCategories.contains(lm.getCategory())) {
169: toRemove.add(lm);
170: }
171: }
172: messs.removeAll(toRemove);
173: rem += toRemove.size();
174: }
175: return rem;
176: }
177:
178: public void remove(LineMessage mess) {
179: messagesForFile.get(mess.getSourceJavaName()).remove(mess);
180: }
181:
182: /** Used during tool analysis. Quick ! no UI update.
183: */
184: public void removeMessagesFor(String javaName, Class type) {
185: ArrayList<LineMessage> messs = messagesForFile.get(javaName);
186: if (messs == null)
187: return;
188:
189: List<LineMessage> toRemove = new ArrayList<LineMessage>();
190: for (final LineMessage lm : messs) // :-( ConcurrentModificationException sometimes onload !
191: {
192: if (lm.getClass() == type) {
193: toRemove.add(lm);
194: }
195: }
196: messs.removeAll(toRemove);
197: }
198:
199: public List<LineMessage> getAllMessagesOfType(Class type) {
200: List<LineMessage> found = new ArrayList<LineMessage>();
201:
202: for (ArrayList<LineMessage> messs : messagesForFile.values()) {
203: if (messs == null)
204: continue;
205: for (LineMessage lm : messs) {
206: if (lm.getClass() == type) {
207: found.add(lm);
208: }
209: }
210: }
211: return found;
212: }
213:
214: /** Used to remove all messages of a certain type in a package (recursively).
215: */
216: public void removeMessagesForJavaNameStartingWith(
217: String javaNameStart, Class type) {
218: for (String jn : messagesForFile.keySet()) {
219: if (!jn.startsWith(javaNameStart))
220: continue;
221:
222: ArrayList<LineMessage> messs = messagesForFile.get(jn);
223: if (messs == null)
224: continue;
225:
226: List<LineMessage> toRemove = new ArrayList<LineMessage>();
227: for (LineMessage lm : messs) {
228: if (lm.getClass() == type) {
229: toRemove.add(lm);
230: }
231: }
232: messs.removeAll(toRemove);
233: }
234: }
235:
236: public List<LineMessage> getMessages(String javaName) {
237: return messagesForFile.get(javaName);
238: }
239:
240: public boolean hasMessages(String javaName) {
241: if (!messagesForFile.containsKey(javaName))
242: return false;
243: return messagesForFile.get(javaName).size() > 0;
244: }
245:
246: /** null if not found */
247: @edu.umd.cs.findbugs.annotations.CheckForNull
248: public LineMessage getMessageAfterLine(String javaName, int line) {
249: List<LineMessage> messs = getMessages(javaName);
250: if (messs == null)
251: return null;
252: Collections.sort(messs);
253: for (LineMessage lm : messs) {
254: if (lm.getLine() > line)
255: return lm;
256: }
257: return null;
258: }
259:
260: /** null if not found */
261: @edu.umd.cs.findbugs.annotations.CheckForNull
262: public LineMessage getFirstMessageLine(String javaName) {
263: List<LineMessage> messs = getMessages(javaName);
264: if (messs == null)
265: return null;
266: if (messs.size() == 0)
267: return null;
268:
269: Collections.sort(messs);
270: return messs.get(0);
271: }
272:
273: /** null if not found */
274: @edu.umd.cs.findbugs.annotations.CheckForNull
275: public LineMessage getLastMessageLine(String javaName) {
276: List<LineMessage> messs = getMessages(javaName);
277: if (messs == null)
278: return null;
279: if (messs.size() == 0)
280: return null;
281:
282: Collections.sort(messs);
283: return messs.get(messs.size() - 1);
284: }
285:
286: /** null if not found */
287: @edu.umd.cs.findbugs.annotations.CheckForNull
288: public LineMessage getMessageBeforeLine(String javaName, int line) {
289: List<LineMessage> messs = getMessages(javaName);
290: if (messs == null)
291: return null;
292: Collections.sort(messs);
293: for (int i = messs.size() - 1; i >= 0; i--) {
294: if (messs.get(i).getLine() <= line)
295: return messs.get(i);
296: }
297: return null;
298: }
299:
300: /** Shifts the LineMessages line numbers one for lines above linePos
301: * TODO: keep track of remove and show in the replaced region the balance of <cr>
302: * This also shift the bookmarks...
303: */
304: public void lineInsertOccured(String javaName, int linePos, int diff) {
305: boolean has = false;
306: List<LineMessage> messs = getMessages(javaName);
307: if (messs == null)
308: return;
309:
310: for (LineMessage mess : messs) {
311: if (mess.getLine() > linePos + 1) {
312: has = true;
313: mess.shiftLine(diff);
314: }
315: }
316:
317: for (SourceBookmark sb : MainEditorFrame.instance
318: .getActualProject().getBookmarksFor(javaName)) {
319: if (sb.getLinePosition() > linePos + 1) {
320: has = true;
321: sb.setLinePosition(sb.getLinePosition() + diff);
322: }
323: }
324:
325: if (has) {
326: refreshView();
327: }
328: }
329:
330: public void lineRemoved(String javaName, int lineStart, int lineEnd) {
331: List<LineMessage> messs = getMessages(javaName);
332: if (messs == null)
333: return;
334:
335: List<LineMessage> toRemove = new ArrayList<LineMessage>();
336: for (LineMessage mess : messs) {
337: if (mess.getLine() >= lineStart
338: && mess.getLine() <= lineEnd) {
339: toRemove.add(mess);
340: }
341: }
342:
343: if (toRemove.size() > 0) {
344: for (LineMessage m : toRemove) {
345: remove(m);
346: }
347: refreshView();
348: }
349:
350: // Don't remove bookmarks!
351: }
352:
353: private StorageVector getStorageRepresentation() {
354: StorageVector sv = new StorageVector();
355: sv.add(1); // 0
356: StorageVector svMess = new StorageVector();
357: sv.add(svMess); // 1
358:
359: for (ArrayList<LineMessage> lms : messagesForFile.values()) {
360: for (LineMessage lm : lms) {
361: svMess.add(lm.getStorageRepresentation());
362: }
363: }
364:
365: // [Sep2007]
366: sv.add(new ArrayList<String>(relevantCategories));
367: sv.add(new ArrayList<String>(irrelevantCategories));
368:
369: return sv;
370: }
371:
372: private void createFromStorageVector(StorageVector sv) {
373: synchronized (instance) {
374: //int version = (Integer) sv.get(0);
375: StorageVector messV = (StorageVector) sv.get(1); // 1
376:
377: for (Object o : messV) {
378: StorageVector mv = (StorageVector) o;
379: String clname = (String) mv.get(1);
380: if (clname.endsWith("FBLineMessage")) {
381: addNC(FBLineMessage.createFromStorageVector(mv));
382: } else if (clname.endsWith("Lint4JLineMessage")) {
383: addNC(Lint4JLineMessage.createFromStorageVector(mv));
384: } else if (clname.endsWith("PMDMessage")) {
385: addNC(PMDMessage.createFromStorageVector(mv));
386: } else if (clname.endsWith("JLintLineMessage")) {
387: addNC(JLintLineMessage.createFromStorageVector(mv));
388: } else if (clname.endsWith("CSLineMessage")) {
389: addNC(CSLineMessage.createFromStorageVector(mv));
390: }
391: /*else if(clname.endsWith("SSTLineMessage"))
392: {
393: addNC( SSTLineMessage.createFromStorageVector( mv ));
394: }*/
395: else if (clname.endsWith("TintLineMessage")) {
396: addNC(TintLineMessage.createFromStorageVector(mv));
397: } else if (clname.endsWith("CompilationMessage")) {
398: addNC(CompilationMessage
399: .createFromStorageVector(mv));
400: } else if (clname.endsWith("JavaDocLineMessage")) {
401: addNC(JavaDocLineMessage
402: .createFromStorageVector(mv));
403: } else if (clname.endsWith("BreakpointLineMessage")) {
404: addNC(BreakpointLineMessage
405: .createFromStorageVector(mv));
406: } else {
407: System.out.println("LMM: Unknown clname=" + clname);
408: }
409: }
410:
411: if (sv.size() > 2) {
412: StorageVector relevantCat = (StorageVector) sv.get(2); // 2
413: StorageVector irrelevantCat = (StorageVector) sv.get(3); // 3
414:
415: relevantCategories.clear();
416: for (Object ci : relevantCat) {
417: relevantCategories.add((String) ci);
418: }
419: irrelevantCategories.clear();
420: for (Object ci : irrelevantCat) {
421: irrelevantCategories.add((String) ci);
422: }
423: }
424: }
425:
426: }
427:
428: /** Called after project close (on exit or when loading another project).
429: */
430: public void storeToFile(ProjectSettings settings) throws Exception {
431:
432: File projFile = MainEditorFrame.instance.getActualProjectFile();
433: //new Throwable("store messages to "+projFile).printStackTrace();
434:
435: if (projFile == null)
436: return; // can occur, when not saving...
437:
438: File dir = settings.getProjectSettingsFolder();
439: if (!dir.exists())
440: dir.mkdirs();
441:
442: File fs = new File(dir, ".messagesz");
443: StorageVector vr = getStorageRepresentation();
444:
445: FileUtils.saveZippedVectorToFile(fs, vr); // [Dec2006]: zipped
446:
447: File oldFile = new File(dir, ".messages");
448: if (oldFile.exists())
449: oldFile.delete();
450: }
451:
452: public void restoreFromFile(ProjectSettings settings)
453: throws Exception {
454:
455: File fs = new File(settings.getProjectSettingsFolder(),
456: ".messagesz");
457: // new Throwable(""+fs).printStackTrace();
458:
459: this .removeAllMessages();
460: refreshView();
461:
462: if (fs.exists()) {
463: try {
464: StorageVector sv = FileUtils
465: .loadZippedVectorFromFile3(fs);
466: this .createFromStorageVector(sv);
467: } catch (Exception e) {
468: throw e;
469: } finally {
470: refreshView();
471: }
472: } else {
473: // old !
474: fs = new File(settings.getProjectSettingsFolder(),
475: ".messages");
476: if (fs.exists()) {
477: try {
478: StorageVector sv = FileUtils.loadVectorFromFile(fs);
479: this .createFromStorageVector(sv);
480: } catch (Exception e) {
481: throw e;
482: } finally {
483: refreshView();
484: }
485: }
486: }
487: }
488:
489: }
|