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

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.