001: package tide.editor.linemessages;
002:
003: import javax.swing.text.TabSet;
004: import snow.utils.gui.Icons;
005: import snow.utils.gui.GUIUtils;
006: import javax.swing.text.TabStop;
007: import snow.utils.gui.CloseControlPanel;
008: import snow.texteditor.SimpleDocument;
009: import snow.utils.DateUtils;
010: import snow.sortabletable.*;
011: import javax.swing.table.*;
012: import java.util.*;
013: import javax.swing.*;
014: import javax.swing.event.*;
015: import java.awt.BorderLayout;
016: import java.awt.EventQueue;
017: import java.awt.Insets;
018: import java.awt.event.*;
019: import tide.compiler.CompilerOutputPanel;
020: import tide.sources.FileItem;
021: import tide.editor.MainEditorFrame;
022:
023: /** Displays all the messages. (singleton added in the output tabs)
024: * TODO: store column sizes
025: */
026: public final class MessagesTable extends JPanel {
027: final private List<LineMessage> messages = new ArrayList<LineMessage>();
028: final private static String[] COLUMN_NAMES = { "line", "message",
029: "priority", "tool", "cat", "age", "relevance" };
030: final private static int[] COLUMN_PREFERED_SIZES = { 20, 40, 2, 4,
031: 5, 4, 4 };
032: final private MessagesTableModel messagesTableModel;
033: final private SortableTableModel sortableTableModel;
034: final JTable table;
035: final JSplitPane splitPane;
036: final CompilerOutputPanel tracePanel = new CompilerOutputPanel();
037: final private MultiSearchPanel asp;
038:
039: private static MessagesTable instance;
040:
041: public static synchronized MessagesTable getInstance() {
042: if (instance == null) {
043: instance = new MessagesTable();
044: }
045: return instance;
046: }
047:
048: private MessagesTable() {
049: super (new BorderLayout(0, 0));
050: setBorder(null);
051:
052: messagesTableModel = new MessagesTableModel();
053: sortableTableModel = new SortableTableModel(messagesTableModel);
054: table = new JTable(sortableTableModel);
055: table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
056: sortableTableModel.installGUI(table);
057:
058: JScrollPane sp = new JScrollPane(table);
059: sp.setBorder(null);
060: sp.getViewport().setBorder(null);
061:
062: splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, sp,
063: tracePanel);
064: splitPane.setOneTouchExpandable(true);
065: EventQueue.invokeLater(new Runnable() {
066: public void run() {
067: splitPane.setDividerLocation(230); //.2 does NOT work. (maybe must wait until shown...)
068: }
069: });
070:
071: add(splitPane, BorderLayout.CENTER);
072: splitPane.setBorder(null);
073:
074: asp = new MultiSearchPanel("Filter: ", null, sortableTableModel);
075: JPanel northPan = new JPanel();
076: northPan.setLayout(new BoxLayout(northPan, BoxLayout.X_AXIS));
077: northPan.add(GUIUtils.wrapLeft(asp, 0));
078:
079: add(northPan, BorderLayout.NORTH);
080:
081: northPan.add(Box.createHorizontalStrut(10));
082: northPan
083: .add(GUIUtils
084: .createHelpLabel("Hint: use CTRL+up/down/1/9 to navigate messages."));
085: northPan.add(Box.createHorizontalStrut(10));
086: final JButton opts = new JButton("Options");
087: northPan.add(opts);
088: opts.setMargin(new Insets(0, 2, 0, 2));
089:
090: opts.addActionListener(new ActionListener() {
091: public void actionPerformed(ActionEvent ae) {
092: JPopupMenu popup = new JPopupMenu();
093:
094: JMenuItem st = new JMenuItem("View statistics",
095: Icons.sharedStat);
096: popup.add(st);
097: st.addActionListener(new ActionListener() {
098: public void actionPerformed(ActionEvent ae) {
099: showStats(false);
100: }
101: });
102:
103: JMenuItem st2 = new JMenuItem(
104: "View statistics of relevant messages",
105: Icons.sharedStat);
106: popup.add(st2);
107: st2.addActionListener(new ActionListener() {
108: public void actionPerformed(ActionEvent ae) {
109: showStats(true);
110: }
111: });
112:
113: JMenuItem cd = new JMenuItem("View defined categories");
114: popup.addSeparator();
115: popup.add(cd);
116: cd.addActionListener(new ActionListener() {
117: public void actionPerformed(ActionEvent ae) {
118: CategoriesTable
119: .showTable(MainEditorFrame.instance);
120: }
121: });
122:
123: JMenuItem cat = new JMenuItem(
124: "Remove messages defined as irrelevant",
125: Icons.sharedCross);
126: popup.addSeparator();
127: popup.add(cat);
128: cat.addActionListener(new ActionListener() {
129: public void actionPerformed(ActionEvent ae) {
130: LineMessagesManager.getInstance()
131: .removeIrrelevantMessages();
132: refresh();
133: }
134: });
135:
136: popup.show(opts, 0, opts.getHeight());
137: }
138: });
139:
140: // show the message
141: table.getSelectionModel().addListSelectionListener(
142: new ListSelectionListener() {
143: public void valueChanged(ListSelectionEvent lse) {
144: if (lse.getValueIsAdjusting())
145: return;
146:
147: int posView = table.getSelectedRow();
148: if (posView == -1) {
149: tracePanel.doc.setText("no selection");
150: return;
151: }
152:
153: int posModel = sortableTableModel
154: .getIndexInUnsortedFromTablePos(posView);
155: if (posModel != -1) {
156: LineMessage lm = messages.get(posModel);
157: tracePanel.doc.setText(lm
158: .getMessageForOutputPanel());
159: }
160: }
161: });
162:
163: table.addMouseListener(new MouseAdapter() {
164: @Override
165: public void mousePressed(MouseEvent me) {
166: if (me.getClickCount() == 2) {
167: //select the line on double click
168: int pos = sortableTableModel
169: .getIndexInUnsortedFromTablePos(table
170: .getSelectedRow());
171: if (pos < 0)
172: return;
173: final LineMessage lm = messagesTableModel
174: .getMessageAt(pos);
175: jumpToSourceForMessage(lm);
176: return;
177: }
178:
179: if (me.isPopupTrigger()) {
180: showTablePopup(me);
181: return;
182: }
183: }
184:
185: @Override
186: public void mouseReleased(MouseEvent me) {
187: if (me.isPopupTrigger()) {
188: showTablePopup(me);
189: return;
190: }
191: }
192: });
193: }
194:
195: /** Also called from editorpanel.
196: */
197: public void jumpToSourceForMessage(LineMessage lm) {
198: FileItem fi = MainEditorFrame.instance.getFileItem(lm
199: .getSourceJavaName(), null);
200: MainEditorFrame.instance.setSourceOrItemToEditOrView(fi, true);
201:
202: int line = lm.getLine();
203: if (line < 1)
204: line = 1; // enforce showing
205: MainEditorFrame.instance.editorPanel.selectLinePart(line - 1,
206: lm.getColumn());
207: }
208:
209: private void showTablePopup(MouseEvent me) {
210: JPopupMenu popup = new JPopupMenu();
211: /*
212: if(table.getSelectedRowCount()==1)
213: {
214: int pos = sortableTableModel.getIndexInUnsortedFromTablePos( table.getSelectedRow() );
215: if(pos<0) return;
216: final LineMessage lm = messagesTableModel.getMessageAt(pos);
217: }*/
218:
219: if (table.getSelectedRowCount() < table.getRowCount()
220: && table.getRowCount() > 1) {
221: JMenuItem sal = new JMenuItem("select all "
222: + table.getRowCount() + " messages");
223: popup.add(sal);
224: sal.addActionListener(new ActionListener() {
225: public void actionPerformed(ActionEvent ae) {
226: table.selectAll();
227: }
228: });
229: }
230:
231: if (table.getSelectedRowCount() > 0) {
232: final List<LineMessage> sel = new ArrayList<LineMessage>();
233: for (int r : table.getSelectedRows()) {
234: int pos = sortableTableModel
235: .getIndexInUnsortedFromTablePos(r);
236: sel.add(messagesTableModel.getMessageAt(pos));
237: }
238: JMenuItem remove = new JMenuItem("remove " + sel.size()
239: + " messages", Icons.sharedCross);
240: popup.addSeparator();
241: popup.add(remove);
242: remove.addActionListener(new ActionListener() {
243: public void actionPerformed(ActionEvent ae) {
244: messagesTableModel.remove(sel);
245: if (sel.size() > 0) {
246: refresh();
247: }
248: }
249: });
250:
251: popup.addSeparator();
252:
253: JMenuItem defR = new JMenuItem(
254: "Define categories as relevant");
255: popup.add(defR);
256: defR.addActionListener(new ActionListener() {
257: public void actionPerformed(ActionEvent ae) {
258: for (LineMessage lm : sel) {
259: LineMessagesManager.getInstance()
260: .getRelevantCategories().add(
261: lm.getCategory());
262: }
263: messagesTableModel.update();
264: }
265: });
266:
267: JMenuItem defIR = new JMenuItem(
268: "Define categories as irrelevant");
269: popup.add(defIR);
270: defIR.addActionListener(new ActionListener() {
271: public void actionPerformed(ActionEvent ae) {
272: for (LineMessage lm : sel) {
273: LineMessagesManager.getInstance()
274: .getIrrelevantCategories().add(
275: lm.getCategory());
276: }
277: messagesTableModel.update();
278: }
279: });
280:
281: popup.addSeparator();
282:
283: JMenuItem defS = new JMenuItem(
284: "Remove relevance definition", Icons.sharedCross);
285: popup.add(defS);
286: defS.addActionListener(new ActionListener() {
287: public void actionPerformed(ActionEvent ae) {
288: for (LineMessage lm : sel) {
289: LineMessagesManager.getInstance()
290: .getIrrelevantCategories().remove(
291: lm.getCategory());
292: LineMessagesManager.getInstance()
293: .getRelevantCategories().remove(
294: lm.getCategory());
295: }
296: messagesTableModel.update();
297: }
298: });
299:
300: }
301:
302: popup.show(table, me.getX(), me.getY());
303: }
304:
305: // called in the EDT !
306: public void selectMessage(LineMessage lm) {
307: int pos = messages.indexOf(lm);
308: if (pos == -1)
309: return;
310: int tp = sortableTableModel.getIndexInFoundFromBasicIndex(pos);
311: if (tp >= 0) {
312: table.getSelectionModel().setSelectionInterval(tp, tp);
313: table.scrollRectToVisible(table.getCellRect(tp, 0, true));
314: }
315: }
316:
317: /** Must be called in the EDT !
318: * directly calls the linepanel to refresh.
319: * called from LineMessManager
320: *
321: * 1) reread all messages from manager
322: * 2) update the count
323: * 3) call the linepanel view refresh
324: */
325: public void refresh() {
326: this .messagesTableModel.refresh();
327: MainEditorFrame.instance.outputPanels.setMessagesCount(messages
328: .size());
329: MainEditorFrame.instance.editorPanel.getLinePanel()
330: .refreshMessages();
331: }
332:
333: public void setFilter(String filter) {
334: asp.setQueries(Query.simpleSearchQuery(filter));
335: }
336:
337: public void requestFocusForSearch() {
338: asp.getFirstTextField().requestFocus();
339: }
340:
341: //@net.jcip.annotations.ThreadSafe
342: public void setShowLatest() {
343: EventQueue.invokeLater(new Runnable() {
344: public void run() {
345: sortableTableModel.setSortedColumnAndOrder(5, false);
346: sortableTableModel.sort(5, false);
347: }
348: });
349: }
350:
351: private void showStats(boolean relevantOnly) {
352: JDialog d = new JDialog(MainEditorFrame.instance,
353: "Messages statistics", false);
354: SimpleDocument doc = new SimpleDocument();
355: JTextPane tp = new JTextPane(doc);
356: tp.setEditable(false);
357: d.add(new JScrollPane(tp), BorderLayout.CENTER);
358:
359: CloseControlPanel ccpd = new CloseControlPanel(d, false, true,
360: "Close");
361: d.add(ccpd, BorderLayout.SOUTH);
362: //d.pack();
363: d.setSize(720, 600);
364: d.setLocationRelativeTo(MainEditorFrame.instance);
365: d.setVisible(true);
366:
367: // fill the stats
368:
369: doc.appendLine("" + messages.size() + " messages");
370: //NO influence here ! doc.setTabsForDoc(20, 10); // todo: tabright for numbers TabStop.
371:
372: // m(tool, m(cat, count))
373:
374: Map<String, HashMap<String, Integer>> categoriesCount = new HashMap<String, HashMap<String, Integer>>();
375: int relm = 0;
376: for (LineMessage mi : messages) {
377: String ci = mi.getCategory();
378: String ti = mi.getMessageOriginator();
379:
380: if (LineMessagesManager.getInstance()
381: .getRelevantCategories().contains(ci)) {
382: relm++;
383: } else if (relevantOnly) {
384: continue;
385: }
386:
387: if (!categoriesCount.containsKey(ti)) {
388: categoriesCount.put(ti, new HashMap<String, Integer>());
389: }
390: Map<String, Integer> catmap = categoriesCount.get(ti);
391: if (!catmap.containsKey(ci)) {
392: catmap.put(ci, 1);
393: } else {
394: catmap.put(ci, 1 + catmap.get(ci));
395: }
396:
397: }
398:
399: doc.appendLine("" + relm + " relevant messages");
400:
401: TreeSet<String> toolNames = new TreeSet<String>(categoriesCount
402: .keySet());
403: for (String ti : toolNames) {
404: Map<String, Integer> catmap = categoriesCount.get(ti);
405: int tot = 0;
406: for (int ni : catmap.values()) {
407: tot += ni;
408: }
409: doc.appendLine("\n" + ti + ": " + tot + " message"
410: + (tot == 1 ? "" : "s"));
411:
412: TreeSet<String> catNames = new TreeSet<String>(catmap
413: .keySet());
414: for (String ci : catNames) {
415: int ni = catmap.get(ci);
416: doc.append("\t" + ni + " \t" + ci); //+" message"+(ni==1?"":"s"));
417:
418: if (LineMessagesManager.getInstance()
419: .getRelevantCategories().contains(ci)) {
420: doc.appendError(" [Relevant]");
421: } else if (LineMessagesManager.getInstance()
422: .getIrrelevantCategories().contains(ci)) {
423: doc.append(" [Irrelevant]");
424: }
425: /*else
426: {
427: }*/
428:
429: doc.append("\r\n");
430: }
431: }
432:
433: TabStop[] tabs = new TabStop[2];
434: tabs[0] = new TabStop(60f, TabStop.ALIGN_RIGHT,
435: TabStop.LEAD_NONE);
436: tabs[1] = new TabStop(75f, TabStop.ALIGN_LEFT,
437: TabStop.LEAD_NONE);
438: TabSet tabSet = new TabSet(tabs);
439: doc.setTabsForDoc(tabSet);
440:
441: tp.setCaretPosition(0);
442: }
443:
444: class MessagesTableModel extends FineGrainTableModel {
445: /*public MessagesTableModel()
446: {
447: //no: refresh();
448: }*/
449:
450: public void refresh() {
451: synchronized (LineMessagesManager.getInstance()) {
452: messages.clear();
453: for (String jn : LineMessagesManager.getInstance().messagesForFile
454: .keySet()) {
455: messages
456: .addAll(LineMessagesManager.getInstance().messagesForFile
457: .get(jn));
458: }
459: }
460: update();
461: }
462:
463: public int getRowCount() {
464: return messages.size();
465: }
466:
467: public int getColumnCount() {
468: return COLUMN_NAMES.length;
469: }
470:
471: public LineMessage getMessageAt(int row) {
472: return messages.get(row);
473: }
474:
475: public void remove(List<LineMessage> mess) {
476: fireTableModelWillChange();
477:
478: for (LineMessage lm : mess) {
479: LineMessagesManager.getInstance().remove(lm);
480: }
481: messages.removeAll(mess);
482:
483: fireTableDataChanged();
484: fireTableModelHasChanged();
485: }
486:
487: public Object getValueAt(int row, int col) {
488: if (row < 0)
489: return "bad row:" + row;
490: if (row >= messages.size())
491: return "bad row:" + row;
492:
493: LineMessage pi = messages.get(row);
494:
495: if (col == 0)
496: return pi.getSourceLineForTableColumn();
497: if (col == 1)
498: return pi.getMessageForTableColumn();
499: if (col == 2)
500: return pi.getPriority();
501: if (col == 3)
502: return pi.getMessageOriginator();
503: if (col == 4)
504: return pi.getCategory();
505: if (col == 5)
506: return DateUtils.formatTimeDifference(System
507: .currentTimeMillis()
508: - pi.created);
509: if (col == 6) {
510: if (LineMessagesManager.getInstance()
511: .getRelevantCategories().contains(
512: pi.getCategory()))
513: return "Relevant";
514: if (LineMessagesManager.getInstance()
515: .getIrrelevantCategories().contains(
516: pi.getCategory()))
517: return "Irrelevant";
518: return "?";
519: }
520:
521: return "?";
522: }
523:
524: public void update() {
525: fireTableDataChanged();
526: fireTableModelHasChanged();
527: }
528:
529: @Override
530: public String getColumnName(int column) {
531: if (column >= 0 && column < COLUMN_NAMES.length)
532: return COLUMN_NAMES[column];
533: return "??";
534: }
535:
536: @Override
537: public int getPreferredColumnWidth(int column) {
538: if (column >= 0 && column < COLUMN_PREFERED_SIZES.length)
539: return COLUMN_PREFERED_SIZES[column];
540: return -1;
541: }
542:
543: @Override
544: public int compareForColumnSort(int pos1, int pos2, int col) {
545: synchronized (LineMessagesManager.getInstance()) // [Oct2007]: missin was causing NPEs
546: {
547: if (pos1 >= messages.size() || pos2 >= messages.size())
548: return 0; // NEEDED, maybe caused by a bad sync problem
549:
550: if (col == 0) {
551:
552: LineMessage m1 = messages.get(pos1);
553: LineMessage m2 = messages.get(pos2);
554: int comp = m1.getSourceJavaName().compareTo(
555: m2.getSourceJavaName());
556: if (comp != 0)
557: return comp;
558: return super .compareDoubles(m1.getLine(), m2
559: .getLine());
560: } else if (col == 5) {
561: LineMessage m1 = messages.get(pos1);
562: LineMessage m2 = messages.get(pos2);
563: if (m1 == null || m2 == null)
564: return 0; // [Oct2007]
565:
566: return Long.valueOf(m1.created).compareTo(
567: m2.created);
568: } else {
569: return super.compareForColumnSort(pos1, pos2, col);
570: }
571: }
572: }
573:
574: }
575:
576: }
|