Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
IngestMessagePanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2014 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.ingest;
20 
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Cursor;
24 import java.awt.Dimension;
25 import java.awt.Font;
26 import java.awt.Graphics;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.beans.PropertyChangeSupport;
30 import java.text.DateFormat;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Date;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import javax.swing.JLabel;
39 import javax.swing.JPanel;
40 import org.openide.util.NbBundle;
42 import javax.swing.JTable;
43 import javax.swing.ListSelectionModel;
44 import javax.swing.SwingConstants;
45 import javax.swing.event.ListSelectionEvent;
46 import javax.swing.event.ListSelectionListener;
47 import javax.swing.event.TableModelEvent;
48 import javax.swing.event.TableModelListener;
49 import javax.swing.table.AbstractTableModel;
50 import javax.swing.table.DefaultTableCellRenderer;
51 import javax.swing.table.TableCellRenderer;
53 import org.sleuthkit.datamodel.BlackboardArtifact;
55 import java.util.logging.Level;
56 
61 class IngestMessagePanel extends JPanel implements TableModelListener {
62 
63  private final MessageTableModel tableModel;
64  private MessageTableRenderer renderer;
65  private final IngestMessageMainPanel mainPanel;
66  private static final Color ERROR_COLOR = new Color(255, 90, 90);
67  private volatile int lastRowSelected = -1;
68  private volatile long totalMessages = 0;
69  private static final Logger logger = Logger.getLogger(IngestMessagePanel.class.getName());
70  private static final PropertyChangeSupport messagePcs = new PropertyChangeSupport(IngestMessagePanel.class);
71  static final String TOTAL_NUM_MESSAGES_CHANGED = "TOTAL_NUM_MESSAGES_CHANGED"; // total number of messages changed NON-NLS
72  static final String MESSAGES_BOX_CLEARED = "MESSAGES_BOX_CLEARED"; // all messaged in inbox were cleared NON-NLS
73  static final String TOTAL_NUM_NEW_MESSAGES_CHANGED = "TOTAL_NUM_NEW_MESSAGES_CHANGED"; // total number of new messages changed NON-NLS
74 
78  public IngestMessagePanel(IngestMessageMainPanel mainPanel) {
79  this.mainPanel = mainPanel;
80  tableModel = new MessageTableModel();
81  initComponents();
82  customizeComponents();
83  }
84 
85  public void markAllSeen() {
86  tableModel.markAllSeen();
87  }
88 
89  int getLastRowSelected() {
90  return this.lastRowSelected;
91  }
92 
93  synchronized IngestMessageGroup getSelectedMessage() {
94  if (lastRowSelected < 0) {
95  return null;
96  }
97 
98  return tableModel.getMessageGroup(lastRowSelected);
99  }
100 
101  synchronized IngestMessageGroup getMessageGroup(int rowNumber) {
102  return tableModel.getMessageGroup(rowNumber);
103  }
104 
105  synchronized static void addPropertyChangeSupportListener(PropertyChangeListener l) {
106  messagePcs.addPropertyChangeListener(l);
107  }
108 
114  @SuppressWarnings("unchecked")
115  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
116  private void initComponents() {
117 
118  jScrollPane1 = new javax.swing.JScrollPane();
119  messageTable = new javax.swing.JTable();
120  controlPanel = new javax.swing.JPanel();
121  sortByLabel = new javax.swing.JLabel();
122  sortByComboBox = new javax.swing.JComboBox<>();
123  totalMessagesNameLabel = new javax.swing.JLabel();
124  totalMessagesNameVal = new javax.swing.JLabel();
125  totalUniqueMessagesNameLabel = new javax.swing.JLabel();
126  totalUniqueMessagesNameVal = new javax.swing.JLabel();
127 
128  setOpaque(false);
129 
130  jScrollPane1.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
131  jScrollPane1.setOpaque(false);
132  jScrollPane1.setPreferredSize(new java.awt.Dimension(32767, 32767));
133 
134  messageTable.setBackground(new java.awt.Color(221, 221, 235));
135  messageTable.setFont(messageTable.getFont().deriveFont(messageTable.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
136  messageTable.setModel(tableModel);
137  messageTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
138  messageTable.setAutoscrolls(false);
139  messageTable.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
140  messageTable.setGridColor(new java.awt.Color(204, 204, 204));
141  messageTable.setOpaque(false);
142  messageTable.setSelectionForeground(new java.awt.Color(0, 0, 0));
143  messageTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
144  messageTable.setShowHorizontalLines(false);
145  messageTable.setShowVerticalLines(false);
146  messageTable.getTableHeader().setReorderingAllowed(false);
147  jScrollPane1.setViewportView(messageTable);
148 
149  sortByLabel.setText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.sortByLabel.text")); // NOI18N
150 
151  sortByComboBox.setFont(sortByComboBox.getFont().deriveFont(sortByComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
152  sortByComboBox.setToolTipText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.sortByComboBox.toolTipText")); // NOI18N
153  sortByComboBox.addActionListener(new java.awt.event.ActionListener() {
154  public void actionPerformed(java.awt.event.ActionEvent evt) {
155  sortByComboBoxActionPerformed(evt);
156  }
157  });
158 
159  totalMessagesNameLabel.setFont(totalMessagesNameLabel.getFont().deriveFont(totalMessagesNameLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
160  totalMessagesNameLabel.setText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.totalMessagesNameLabel.text")); // NOI18N
161 
162  totalMessagesNameVal.setFont(totalMessagesNameVal.getFont().deriveFont(totalMessagesNameVal.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
163  totalMessagesNameVal.setText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.totalMessagesNameVal.text")); // NOI18N
164 
165  totalUniqueMessagesNameLabel.setFont(totalUniqueMessagesNameLabel.getFont().deriveFont(totalUniqueMessagesNameLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
166  totalUniqueMessagesNameLabel.setText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.totalUniqueMessagesNameLabel.text")); // NOI18N
167 
168  totalUniqueMessagesNameVal.setFont(totalUniqueMessagesNameVal.getFont().deriveFont(totalUniqueMessagesNameVal.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
169  totalUniqueMessagesNameVal.setText(org.openide.util.NbBundle.getMessage(IngestMessagePanel.class, "IngestMessagePanel.totalUniqueMessagesNameVal.text")); // NOI18N
170 
171  javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
172  controlPanel.setLayout(controlPanelLayout);
173  controlPanelLayout.setHorizontalGroup(
174  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
175  .addGroup(controlPanelLayout.createSequentialGroup()
176  .addGap(10, 10, 10)
177  .addComponent(sortByLabel)
178  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
179  .addComponent(sortByComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
180  .addGap(101, 101, 101)
181  .addComponent(totalMessagesNameLabel)
182  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
183  .addComponent(totalMessagesNameVal, javax.swing.GroupLayout.DEFAULT_SIZE, 24, Short.MAX_VALUE)
184  .addGap(22, 22, 22)
185  .addComponent(totalUniqueMessagesNameLabel)
186  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
187  .addComponent(totalUniqueMessagesNameVal, javax.swing.GroupLayout.DEFAULT_SIZE, 24, Short.MAX_VALUE)
188  .addGap(22, 22, 22))
189  );
190  controlPanelLayout.setVerticalGroup(
191  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
192  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
193  .addComponent(sortByComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
194  .addComponent(sortByLabel)
195  .addComponent(totalUniqueMessagesNameLabel)
196  .addComponent(totalUniqueMessagesNameVal)
197  .addComponent(totalMessagesNameLabel)
198  .addComponent(totalMessagesNameVal))
199  );
200 
201  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
202  this.setLayout(layout);
203  layout.setHorizontalGroup(
204  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
205  .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
206  .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE)
207  );
208  layout.setVerticalGroup(
209  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
210  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
211  .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE)
212  .addGap(0, 0, 0)
213  .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
214  );
215  }// </editor-fold>//GEN-END:initComponents
216 
217  private void sortByComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sortByComboBoxActionPerformed
218  synchronized (this) {
219  if (sortByComboBox.getSelectedIndex() == 0) {
220  tableModel.reSort(true);
221  } else {
222  tableModel.reSort(false);
223  }
224  }
225  }//GEN-LAST:event_sortByComboBoxActionPerformed
226  // Variables declaration - do not modify//GEN-BEGIN:variables
227  private javax.swing.JPanel controlPanel;
228  private javax.swing.JScrollPane jScrollPane1;
229  private javax.swing.JTable messageTable;
230  private javax.swing.JComboBox<String> sortByComboBox;
231  private javax.swing.JLabel sortByLabel;
232  private javax.swing.JLabel totalMessagesNameLabel;
233  private javax.swing.JLabel totalMessagesNameVal;
234  private javax.swing.JLabel totalUniqueMessagesNameLabel;
235  private javax.swing.JLabel totalUniqueMessagesNameVal;
236  // End of variables declaration//GEN-END:variables
237 
238  private void customizeComponents() {
239  mainPanel.setOpaque(true);
240  jScrollPane1.setOpaque(true);
241  messageTable.setOpaque(false);
242 
247  sortByComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] {
248  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.sortByComboBox.model.time"),
249  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.sortByComboBox.model.priority")}));
250 
251  jScrollPane1.setWheelScrollingEnabled(true);
252 
253  messageTable.setAutoscrolls(false);
254  messageTable.setShowHorizontalLines(false);
255  messageTable.setShowVerticalLines(false);
256 
257  messageTable.getParent().setBackground(messageTable.getBackground());
258 
259  renderer = new MessageTableRenderer();
260  int numCols = messageTable.getColumnCount();
261 
262  // add the cell renderer to all columns
263  for (int i = 0; i < numCols; ++i) {
264  messageTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
265  }
266 
267  messageTable.setCellSelectionEnabled(false);
268  messageTable.setColumnSelectionAllowed(false);
269  messageTable.setRowSelectionAllowed(true);
270  messageTable.getSelectionModel().addListSelectionListener(new MessageVisitedSelection());
271 
272  //this should be done at the end to make it easy to initialize before events are handled
273  tableModel.addTableModelListener(this);
274  }
275 
276  @Override
277  public void paint(Graphics g) {
278  super.paint(g);
279 
280  //workaround to force table resize when window resizes. Use better layout instead?
281  setTableSize(messageTable.getParent().getSize());
282  }
283 
284  @Override
285  public void setPreferredSize(Dimension dmnsn) {
286  super.setPreferredSize(dmnsn);
287  setTableSize(messageTable.getParent().getSize());
288  }
289 
290  void setTableSize(Dimension d) {
291  double[] columnWidths = new double[]{0.20, 0.08, 0.08, 0.49, 0.15};
292  int numCols = messageTable.getColumnCount();
293  for (int i = 0; i < numCols; ++i) {
294  messageTable.getColumnModel().getColumn(i).setPreferredWidth((int) (d.width * columnWidths[i]));
295  }
296  }
297 
298  public synchronized void addMessage(IngestMessage m) {
299  tableModel.addMessage(m);
300 
301  //update total individual messages count
302  ++totalMessages;
303  final int newMsgUnreadUnique = tableModel.getNumberUnreadGroups();
304 
305  try {
306  messagePcs.firePropertyChange(TOTAL_NUM_MESSAGES_CHANGED, 0, newMsgUnreadUnique);
307  } catch (Exception e) {
308  logger.log(Level.SEVERE, "IngestMessagePanel listener threw exception", e); //NON-NLS
309  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "IngestMessagePanel.moduleErr"),
310  NbBundle.getMessage(this.getClass(),
311  "IngestMessagePanel.moduleErr.errListenUpdates.text"),
312  MessageNotifyUtil.MessageType.ERROR);
313  }
314 
315  //update labels
316  this.totalMessagesNameVal.setText(Long.toString(totalMessages));
317  final int totalMessagesUnique = tableModel.getNumberGroups();
318  this.totalUniqueMessagesNameVal.setText(Integer.toString(totalMessagesUnique));
319  }
320 
321  public synchronized void clearMessages() {
322  final int origMsgGroups = tableModel.getNumberUnreadGroups();
323  totalMessages = 0;
324  tableModel.clearMessages();
325  totalMessagesNameVal.setText("-");
326  totalUniqueMessagesNameVal.setText("-");
327 
328  try {
329  messagePcs.firePropertyChange(MESSAGES_BOX_CLEARED, origMsgGroups, 0);
330  } catch (Exception e) {
331  logger.log(Level.SEVERE, "IngestMessagePanel listener threw exception", e); //NON-NLS
332  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "IngestMessagePanel.moduleErr"),
333  NbBundle.getMessage(this.getClass(),
334  "IngestMessagePanel.moduleErr.errListenUpdates.text"),
335  MessageNotifyUtil.MessageType.ERROR);
336  }
337  }
338 
339  public synchronized int getMessagesCount() {
340  return tableModel.getNumberMessages();
341  }
342 
343  private synchronized void setVisited(int rowNumber) {
344  final int origMsgGroups = tableModel.getNumberUnreadGroups();
345  tableModel.setVisited(rowNumber);
346  lastRowSelected = rowNumber;
347 
348  try {
349  messagePcs.firePropertyChange(TOOL_TIP_TEXT_KEY, origMsgGroups, tableModel.getNumberUnreadGroups());
350  } catch (Exception e) {
351  logger.log(Level.SEVERE, "IngestMessagePanel listener threw exception", e); //NON-NLS
352  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "IngestMessagePanel.moduleErr"),
353  NbBundle.getMessage(this.getClass(),
354  "IngestMessagePanel.moduleErr.errListenUpdates.text"),
355  MessageNotifyUtil.MessageType.ERROR);
356  }
357  }
358 
359  @Override
360  public void tableChanged(TableModelEvent e) {
361  int newMessages = tableModel.getNumberNewMessages();
362 
363  try {
364  messagePcs.firePropertyChange(new PropertyChangeEvent(tableModel, TOTAL_NUM_NEW_MESSAGES_CHANGED, -1, newMessages));
365  } catch (Exception ee) {
366  logger.log(Level.SEVERE, "IngestMessagePanel listener threw exception", ee); //NON-NLS
367  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "IngestMessagePanel.moduleErr"),
368  NbBundle.getMessage(this.getClass(),
369  "IngestMessagePanel.moduleErr.errListenUpdates.text"),
370  MessageNotifyUtil.MessageType.ERROR);
371  }
372  }
373 
374  private class MessageTableModel extends AbstractTableModel {
375 
376  private final String[] columnNames = new String[]{
377  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.MsgTableMod.colNames.module"),
378  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.MsgTableMod.colNames.num"),
379  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.MsgTableMod.colNames.new"),
380  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.MsgTableMod.colNames.subject"),
381  NbBundle.getMessage(this.getClass(), "IngestMessagePanel.MsgTableMod.colNames.timestamp")};
382  private final List<TableEntry> messageData = new ArrayList<>();
383  //for keeping track of messages to group, per module, by uniqness
384  private final Map<String, Map<String, List<IngestMessageGroup>>> groupings = new HashMap<>();
385  private boolean chronoSort = true; //chronological sort default
386  private static final int MESSAGE_GROUP_THRESH = 3; //group messages after 3 messages per module with same uniqness
387  private final Logger logger = Logger.getLogger(MessageTableModel.class.getName());
388 
389  @Override
390  public int getColumnCount() {
391  return columnNames.length;
392  }
393 
394  @Override
395  public synchronized int getRowCount() {
396  return getNumberGroups();
397  }
398 
399  public synchronized void markAllSeen() {
400  for (TableEntry entry : messageData) {
401  entry.hasBeenSeen(true);
402  }
403  fireTableChanged(new TableModelEvent(this));
404  }
405 
406  public synchronized int getNumberNewMessages() {
407  int newMessages = 0;
408  for (TableEntry entry : messageData) {
409  if (!entry.hasBeenSeen()) {
410  ++newMessages;
411  }
412  }
413  return newMessages;
414  }
415 
416  public synchronized int getNumberGroups() {
417  return messageData.size();
418  }
419 
420  public synchronized int getNumberMessages() {
421  int total = 0;
422  for (TableEntry e : messageData) {
423  total += e.messageGroup.getCount();
424  }
425  return total;
426  }
427 
428  public synchronized int getNumberUnreadMessages() {
429  int total = 0;
430  for (TableEntry e : messageData) {
431  if (!e.hasBeenVisited) {
432  total += e.messageGroup.getCount();
433  }
434  }
435  return total;
436  }
437 
438  public synchronized int getNumberUnreadGroups() {
439  int total = 0;
440  for (TableEntry e : messageData) {
441  if (!e.hasBeenVisited) {
442  ++total;
443  }
444  }
445  return total;
446  }
447 
448  @Override
449  public String getColumnName(int column) {
450  return columnNames[column];
451  }
452 
453  @Override
454  public synchronized Object getValueAt(int rowIndex, int columnIndex) {
455  Object ret = null;
456 
457  int numMessages = messageData.size();
458  if (rowIndex > messageData.size() - 1
459  || columnIndex > columnNames.length - 1) {
460  //temporary check if the rare case still occurrs
461  //#messages is now lower after last regrouping, and gui event thinks it's not
462  logger.log(Level.WARNING, "Requested inbox message at" + rowIndex, ", only have " + numMessages); //NON-NLS
463  return "";
464  }
465  TableEntry entry = messageData.get(rowIndex);
466 
467  switch (columnIndex) {
468  case 0:
469  ret = entry.messageGroup.getSource();
470  break;
471  case 1:
472  ret = entry.messageGroup.getCount();
473  break;
474  case 2:
475  ret = !entry.hasBeenSeen();
476  break;
477  case 3:
478  ret = entry.messageGroup.getSubject();
479  break;
480  case 4:
481  ret = entry.messageGroup.getDatePosted();
482  break;
483  default:
484  logger.log(Level.SEVERE, "Invalid table column index: {0}", columnIndex); //NON-NLS
485  break;
486  }
487  return ret;
488  }
489 
490  @Override
491  public boolean isCellEditable(int rowIndex, int columnIndex) {
492  return false;
493  }
494 
495  @Override
496  public Class<?> getColumnClass(int c) {
497  return getValueAt(0, c).getClass();
498  }
499 
500  private synchronized int getTableEntryIndex(String uniqueKey) {
501  int ret = -1;
502  int i = 0;
503  for (TableEntry e : messageData) {
504  if (e.messageGroup.getUniqueKey().equals(uniqueKey)) {
505  ret = i;
506  break;
507  }
508  ++i;
509  }
510  return ret;
511  }
512 
513  public synchronized void addMessage(IngestMessage m) {
514  //check how many messages per module with the same uniqness
515  //and add to existing group or create a new group
516  String moduleName = m.getSource();
517  IngestMessageGroup messageGroup = null;
518  if (moduleName != null && m.getMessageType() == IngestMessage.MessageType.DATA) {
519  //not a manager message, a data message, then group
520  if (!groupings.containsKey(moduleName)) {
521  groupings.put(moduleName, new HashMap<String, List<IngestMessageGroup>>());
522  }
523  final Map<String, List<IngestMessageGroup>> groups = groupings.get(moduleName);
524  //groups for this uniqueness
525  final String uniqueness = m.getUniqueKey();
526  List<IngestMessageGroup> uniqGroups = groups.get(uniqueness);
527  if (uniqGroups == null) {
528  //first one with this uniqueness
529  uniqGroups = new ArrayList<>();
530  messageGroup = new IngestMessageGroup(m);
531  uniqGroups.add(messageGroup);
532  groups.put(uniqueness, uniqGroups);
533  } else {
534  final int uniqueGroupsCount = uniqGroups.size();
535  if (uniqueGroupsCount > MESSAGE_GROUP_THRESH) {
536  //merge them
537  messageGroup = uniqGroups.get(0);
538  for (int i = 1; i < uniqueGroupsCount; ++i) {
539  messageGroup.addAll(uniqGroups.get(i));
540  }
541  //add the new msg
542  messageGroup.add(m);
543  //remove merged groups
544  uniqGroups.clear();
545 
546  //add the group with all messages merged
547  uniqGroups.add(messageGroup);
548 
549  //remove all rows with this uniquness, new merged row will be added to the bottom
550  int toRemove;
551  while ((toRemove = getTableEntryIndex(uniqueness)) != -1) {
552  messageData.remove(toRemove);
553  //remove the row, will be added to the bottom
554  this.fireTableRowsDeleted(toRemove, toRemove);
555  }
556 
557  } else if (uniqueGroupsCount == 1) {
558  IngestMessageGroup first = uniqGroups.get(0);
559  //one group with multiple messages
560  if (first.getCount() > 1) {
561  //had already been merged
562  first.add(m);
563  messageGroup = first;
564  //move to bottom of table
565  //remove from existing position
566  int toRemove = 0;
567  while ((toRemove = getTableEntryIndex(uniqueness)) != -1) {
568  messageData.remove(toRemove);
569  //remove the row, will be added to the bottom
570  this.fireTableRowsDeleted(toRemove, toRemove);
571  }
572 
573  } else {
574  //one group with one message
575  //create another group
576  messageGroup = new IngestMessageGroup(m);
577  uniqGroups.add(messageGroup);
578 
579  }
580  } else {
581  //multiple groups with 1 msg each
582  //create another group, until need to merge
583  messageGroup = new IngestMessageGroup(m);
584  uniqGroups.add(messageGroup);
585  //add to bottom
586  }
587  }
588 
589  } else {
590  //manager or non-data message
591  messageGroup = new IngestMessageGroup(m);
592  }
593 
594  //add new or updated row to the bottom
595  messageData.add(new TableEntry(messageGroup));
596  int newRowIndex = messageData.size() - 1;
597  fireTableRowsInserted(newRowIndex, newRowIndex);
598 
599  //if priority sort, need to re-sort everything
600  if (chronoSort == false) {
601  Collections.sort(messageData);
602  fireTableDataChanged();
603  }
604  }
605 
606  public synchronized void clearMessages() {
607  messageData.clear();
608  groupings.clear();
609  fireTableDataChanged();
610  }
611 
612  public synchronized void setVisited(int rowNumber) {
613  messageData.get(rowNumber).hasBeenVisited(true);
614  //repaint the cell
615  fireTableCellUpdated(rowNumber, 2);
616  }
617 
618  public synchronized void setVisitedAll() {
619  int row = 0;
620  for (TableEntry e : messageData) {
621  if (!e.hasBeenVisited) {
622  e.hasBeenVisited(true);
623  fireTableCellUpdated(row, 2);
624  }
625  ++row;
626  }
627  }
628 
629  public synchronized boolean isVisited(int rowNumber) {
630  if (rowNumber < messageData.size()) {
631  return messageData.get(rowNumber).hasBeenVisited();
632  } else {
633  return false;
634  }
635  }
636 
637  public synchronized MessageType getMessageType(int rowNumber) {
638  if (rowNumber < messageData.size()) {
639  return messageData.get(rowNumber).messageGroup.getMessageType();
640  } else {
641  return null;
642  }
643  }
644 
645  public synchronized IngestMessageGroup getMessageGroup(int rowNumber) {
646  if (rowNumber < messageData.size()) {
647  return messageData.get(rowNumber).messageGroup;
648  } else {
649  return null;
650  }
651  }
652 
653  public synchronized void reSort(boolean chronoLogical) {
654  if (chronoSort == chronoLogical) {
655  return;
656  }
657 
658  chronoSort = chronoLogical;
659  Collections.sort(messageData);
660  fireTableDataChanged();
661  }
662 
663  class TableEntry implements Comparable<TableEntry> {
664 
665  IngestMessageGroup messageGroup;
666  boolean hasBeenVisited = false;
667  boolean hasBeenSeen = false;
668 
669  public boolean hasBeenVisited() {
670  return hasBeenVisited;
671  }
672 
673  public void hasBeenVisited(boolean visited) {
674  hasBeenVisited = visited;
675  }
676 
677  public boolean hasBeenSeen() {
678  return hasBeenSeen;
679  }
680 
681  public void hasBeenSeen(boolean seen) {
682  hasBeenSeen = seen;
683  }
684 
685  TableEntry(IngestMessageGroup messageGroup) {
686  this.messageGroup = messageGroup;
687  }
688 
689  @Override
690  public int compareTo(TableEntry o) {
691  if (chronoSort == true) {
692  return this.messageGroup.getDatePosted().compareTo(o.messageGroup.getDatePosted());
693  } else {
694  return messageGroup.getCount() - o.messageGroup.getCount();
695  }
696  }
697  }
698  }
699 
700  //represents grouping of similar messages
701  //with the same uniqness
702  static class IngestMessageGroup {
703 
704  static final Color VERY_HIGH_PRI_COLOR = new Color(164, 164, 202); //for a single message in a group
705  static final Color HIGH_PRI_COLOR = new Color(180, 180, 211);
706  static final Color MED_PRI_COLOR = new Color(199, 199, 222);
707  static final Color LOW_PRI_COLOR = new Color(221, 221, 235);
708  private final List<IngestMessage> messages;
709 
710  IngestMessageGroup(IngestMessage message) {
711  messages = new ArrayList<>();
712  messages.add(message);
713  }
714 
715  private List<IngestMessage> getMessages() {
716  return messages;
717  }
718 
719  synchronized void add(IngestMessage message) {
720  messages.add(message);
721  }
722 
723  //add all messages from another group
724  synchronized void addAll(IngestMessageGroup group) {
725  for (IngestMessage m : group.getMessages()) {
726  messages.add(m);
727  }
728  }
729 
730  synchronized int getCount() {
731  return messages.size();
732  }
733 
734  synchronized String getDetails() {
735  StringBuilder b = new StringBuilder("");
736  for (IngestMessage m : messages) {
737  String details = m.getDetails();
738  if (details == null || details.equals("")) {
739  continue;
740  }
741  b.append(details);
742  b.append("<br />"); //NON-NLS
743  b.append("<hr />"); //NON-NLS
744  }
745 
746  return b.toString();
747  }
748 
754  synchronized Color getColor() {
755  int count = messages.size();
756  if (count == 1) {
757  return VERY_HIGH_PRI_COLOR;
758  } else if (count < 5) {
759  return HIGH_PRI_COLOR;
760  } else if (count < 15) {
761  return MED_PRI_COLOR;
762  } else {
763  return LOW_PRI_COLOR;
764  }
765  }
766 
772  synchronized Date getDatePosted() {
773  return messages.get(messages.size() - 1).getDatePosted();
774  }
775 
781  synchronized String getSubject() {
782  return messages.get(0).getSubject();
783  }
784 
785  /*
786  * return unique key, should be the same for all msgs
787  */
788  synchronized String getUniqueKey() {
789  return messages.get(0).getUniqueKey();
790  }
791 
792  /*
793  * return source module, should be the same for all msgs
794  */
795  synchronized String getSource() {
796  return messages.get(0).getSource();
797  }
798 
799  /*
800  * return data of the first message
801  */
802  synchronized BlackboardArtifact getData() {
803  return messages.get(0).getData();
804  }
805 
806  /*
807  * return message type, should be the same for all msgs
808  */
809  synchronized IngestMessage.MessageType getMessageType() {
810  return messages.get(0).getMessageType();
811  }
812  }
813 
814  /*
815  * Main TableCellRenderer to be used for ingest message inbox. Delegates to
816  * other TableCellRenderers based different factors such as column data type
817  * or column number.
818  */
819  private class MessageTableRenderer extends DefaultTableCellRenderer {
820 
821  private final TableCellRenderer booleanRenderer = new BooleanRenderer();
822  private final TableCellRenderer defaultRenderer = new DefaultRenderer();
823  private final TableCellRenderer dateRenderer = new DateRenderer();
824  protected int rowSelected;
825 
826  public void setRowSelected(int rowSelected) {
827  this.rowSelected = rowSelected;
828  }
829 
830  @Override
831  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
832  if (value instanceof Boolean) {
833  return booleanRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
834  } else if (value instanceof Date) {
835  return dateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
836  } else {
837  return defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
838  }
839  }
840  }
841 
842  /*
843  * TableCellRenderer used to render boolean values with a bullet point.
844  */
845  private class BooleanRenderer extends DefaultTableCellRenderer {
846 
847  private final String bulletChar = new String(Character.toChars(0x2022));
848 
849  @Override
850  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
851 
852  super.setForeground(table.getSelectionForeground());
853  super.setBackground(table.getSelectionBackground());
854 
855  boolean boolVal;
856  if (value instanceof Boolean) {
857  boolVal = ((Boolean) value);
858  } else {
859  throw new RuntimeException(NbBundle.getMessage(this.getClass(),
860  "IngestMessagePanel.BooleanRenderer.exception.nonBoolVal.msg"));
861  }
862 
863  String aValue = boolVal ? bulletChar : "";
864 
865  JLabel cell = (JLabel) super.getTableCellRendererComponent(table, aValue, isSelected, hasFocus, row, column);
866 
867  // center the bullet in the JLabel
868  cell.setHorizontalAlignment(SwingConstants.CENTER);
869 
870  // increase the font size
871  cell.setFont(cell.getFont().deriveFont(Font.PLAIN, 16));
872 
873  final IngestMessageGroup messageGroup = tableModel.getMessageGroup(row);
874  if (messageGroup != null) {
875  MessageType mt = messageGroup.getMessageType();
876  if (mt == MessageType.ERROR) {
877  cell.setBackground(ERROR_COLOR);
878  } else if (mt == MessageType.WARNING) {
879  cell.setBackground(Color.orange);
880  } else {
881  //cell.setBackground(table.getBackground());
882  cell.setBackground(messageGroup.getColor());
883  }
884  }
885  return cell;
886  }
887  }
888 
893  private class DefaultRenderer extends DefaultTableCellRenderer {
894 
895  @Override
897  JTable table, Object value,
898  boolean isSelected, boolean hasFocus,
899  int row, int column) {
900 
901  Component cell = super.getTableCellRendererComponent(
902  table, value, false, false, row, column);
903 
904  Font visitedFont = cell.getFont().deriveFont(Font.PLAIN, 12);
905  Font notVisitedFont = cell.getFont().deriveFont(Font.BOLD, 12);
906 
907  if (column == 3) {
908  String subject = (String) value;
909  setToolTipText(subject);
910  if (tableModel.isVisited(row)) {
911  cell.setFont(visitedFont);
912  } else {
913  cell.setFont(notVisitedFont);
914  }
915  }
916 
917  final IngestMessageGroup messageGroup = tableModel.getMessageGroup(row);
918  if (messageGroup != null) {
919  MessageType mt = messageGroup.getMessageType();
920  if (mt == MessageType.ERROR) {
921  cell.setBackground(ERROR_COLOR);
922  } else if (mt == MessageType.WARNING) {
923  cell.setBackground(Color.orange);
924  } else {
925  //cell.setBackground(table.getBackground());
926  cell.setBackground(messageGroup.getColor());
927  }
928  }
929  return cell;
930  }
931  }
932 
933  private class DateRenderer extends DefaultTableCellRenderer {
934 
935  @Override
936  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
937 
938  super.setForeground(table.getSelectionForeground());
939  super.setBackground(table.getSelectionBackground());
940 
941  Object aValue = value;
942  if (value instanceof Date) {
943  Date date = (Date) value;
944  DateFormat df = new SimpleDateFormat("HH:mm:ss");
945  aValue = df.format(date);
946  } else {
947  throw new RuntimeException(NbBundle.getMessage(this.getClass(),
948  "IngestMessagePanel.DateRenderer.exception.nonDateVal.text"));
949  }
950 
951  Component cell = super.getTableCellRendererComponent(table, aValue, isSelected, hasFocus, row, column);
952 
953  final IngestMessageGroup messageGroup = tableModel.getMessageGroup(row);
954  if (messageGroup != null) {
955  MessageType mt = messageGroup.getMessageType();
956  if (mt == MessageType.ERROR) {
957  cell.setBackground(ERROR_COLOR);
958  } else if (mt == MessageType.WARNING) {
959  cell.setBackground(Color.orange);
960  } else {
961  //cell.setBackground(table.getBackground());
962  cell.setBackground(messageGroup.getColor());
963  }
964  }
965 
966  return cell;
967  }
968  }
969 
973  private class MessageVisitedSelection implements ListSelectionListener {
974 
975  @Override
976  public void valueChanged(ListSelectionEvent e) {
977  ListSelectionModel selModel = (ListSelectionModel) e.getSource();
978  if (selModel.isSelectionEmpty() || selModel.getValueIsAdjusting()) {
979  return;
980  }
981 
982  final int minIndex = selModel.getMinSelectionIndex();
983  final int maxIndex = selModel.getMaxSelectionIndex();
984  int selected = -1;
985  for (int i = minIndex; i <= maxIndex; i++) {
986  if (selModel.isSelectedIndex(i)) {
987  selected = i;
988  break;
989  }
990  }
991  selModel.clearSelection();
992  if (selected != -1) {
993  setVisited(selected);
994  messageTable.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
995  //check if has details
996  IngestMessageGroup m = getMessageGroup(selected);
997  if (m != null) {
998  String details = m.getDetails();
999  if (details != null && !details.equals("")) {
1000  mainPanel.showDetails(selected);
1001  }
1002  }
1003  messageTable.setCursor(null);
1004  }
1005  }
1006  }
1007 }
synchronized IngestMessageGroup getMessageGroup(int rowNumber)
final Map< String, Map< String, List< IngestMessageGroup > > > groupings
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
synchronized Object getValueAt(int rowIndex, int columnIndex)

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.