Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DefaultTableArtifactContentViewer.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2021 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.contentviewers.artifactviewers;
20
21import java.awt.Component;
22import java.awt.Cursor;
23import java.awt.Toolkit;
24import java.awt.event.ActionEvent;
25import java.awt.event.ActionListener;
26import java.awt.datatransfer.StringSelection;
27import java.text.SimpleDateFormat;
28import java.util.ArrayList;
29import java.util.Enumeration;
30import java.util.List;
31import java.util.logging.Level;
32import javax.swing.JMenuItem;
33import javax.swing.JTextArea;
34import javax.swing.event.ChangeEvent;
35import javax.swing.event.ListSelectionEvent;
36import javax.swing.event.TableColumnModelEvent;
37import javax.swing.table.DefaultTableModel;
38import javax.swing.table.TableColumn;
39import javax.swing.event.TableColumnModelListener;
40import javax.swing.text.View;
41import org.apache.commons.lang.StringUtils;
42import org.openide.util.NbBundle;
43import org.sleuthkit.autopsy.coreutils.Logger;
44import org.sleuthkit.datamodel.BlackboardArtifact;
45import org.sleuthkit.datamodel.BlackboardAttribute;
46import org.sleuthkit.datamodel.Content;
47import org.sleuthkit.datamodel.TskCoreException;
48import org.netbeans.swing.etable.ETable;
49import com.google.gson.JsonElement;
50import com.google.gson.JsonObject;
51import com.google.gson.JsonParser;
52import com.google.gson.JsonArray;
53import java.util.Locale;
54import java.util.Map;
55import javax.swing.SwingUtilities;
56import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
57import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
58//import org.sleuthkit.autopsy.contentviewers.Bundle;
59
63@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
65
66 @NbBundle.Messages({
67 "DefaultTableArtifactContentViewer.attrsTableHeader.type=Type",
68 "DefaultTableArtifactContentViewer.attrsTableHeader.value=Value",
69 "DefaultTableArtifactContentViewer.attrsTableHeader.sources=Source(s)",
70 "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
71 "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
72 })
73
74 private final static Logger logger = Logger.getLogger(DefaultTableArtifactContentViewer.class.getName());
75
76 private static final long serialVersionUID = 1L;
77
78 private static final String[] COLUMN_HEADERS = {
79 Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_type(),
80 Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_value(),
81 Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_sources()};
82 private static final int[] COLUMN_WIDTHS = {100, 800, 100};
83 private static final int CELL_BOTTOM_MARGIN = 5;
84 private static final int CELL_RIGHT_MARGIN = 1;
85
89 resultsTableScrollPane.setViewportView(resultsTable);
92 resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
93 }
94
95 private void initResultsTable() {
96 resultsTable = new ETable();
97 resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
98 private static final long serialVersionUID = 1L;
99
100 @Override
101 public boolean isCellEditable(int rowIndex, int columnIndex) {
102 return false;
103 }
104 });
105 resultsTable.setCellSelectionEnabled(true);
106 resultsTable.getTableHeader().setReorderingAllowed(false);
107 resultsTable.setColumnHidingAllowed(false);
108 resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
109 resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
110
111 @Override
112 public void columnAdded(TableColumnModelEvent e) {
113 // do nothing
114 }
115
116 @Override
117 public void columnRemoved(TableColumnModelEvent e) {
118 // do nothing
119 }
120
121 @Override
122 public void columnMoved(TableColumnModelEvent e) {
123 // do nothing
124 }
125
126 @Override
127 public void columnMarginChanged(ChangeEvent e) {
128 updateRowHeights(); //When the user changes column width we may need to resize row height
129 }
130
131 @Override
132 public void columnSelectionChanged(ListSelectionEvent e) {
133 // do nothing
134 }
135 });
136 resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
137
138 }
139
143 private void updateRowHeights() {
144 int valueColIndex = -1;
145 for (int col = 0; col < resultsTable.getColumnCount(); col++) {
146 if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
147 valueColIndex = col;
148 }
149 }
150 if (valueColIndex != -1) {
151 for (int row = 0; row < resultsTable.getRowCount(); row++) {
152 Component comp = resultsTable.prepareRenderer(
153 resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
154 final int rowHeight;
155 if (comp instanceof JTextArea) {
156 final JTextArea tc = (JTextArea) comp;
157 final View rootView = tc.getUI().getRootView(tc);
158 java.awt.Insets i = tc.getInsets();
159 rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
160 .getWidth() - (i.left + i.right + CELL_RIGHT_MARGIN), //current width minus borders
161 Integer.MAX_VALUE);
162 rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
163 } else {
164 rowHeight = comp.getPreferredSize().height;
165 }
166 if (rowHeight > 0) {
167 resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
168 }
169 }
170 }
171 }
172
176 private void updateColumnSizes() {
177 Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
178 while (columns.hasMoreElements()) {
179 TableColumn col = columns.nextElement();
180 if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
181 col.setPreferredWidth(COLUMN_WIDTHS[0]);
182 } else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
183 col.setPreferredWidth(COLUMN_WIDTHS[1]);
184 } else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
185 col.setPreferredWidth(COLUMN_WIDTHS[2]);
186 }
187 }
188 }
189
195 @SuppressWarnings("unchecked")
196 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
197 private void initComponents() {
198
199 rightClickMenu = new javax.swing.JPopupMenu();
200 copyMenuItem = new javax.swing.JMenuItem();
201 selectAllMenuItem = new javax.swing.JMenuItem();
202 resultsTableScrollPane = new javax.swing.JScrollPane();
203
204 copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultTableArtifactContentViewer.class, "DefaultTableArtifactContentViewer.copyMenuItem.text")); // NOI18N
206
207 selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultTableArtifactContentViewer.class, "DefaultTableArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
209
210 setPreferredSize(new java.awt.Dimension(0, 0));
211
212 resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
213 resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
214 resultsTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 0));
215 resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(0, 0));
216
217 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
218 this.setLayout(layout);
219 layout.setHorizontalGroup(
220 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
221 .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
222 );
223 layout.setVerticalGroup(
224 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
225 .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
226 );
227 }// </editor-fold>//GEN-END:initComponents
228
229 // Variables declaration - do not modify//GEN-BEGIN:variables
230 private javax.swing.JMenuItem copyMenuItem;
231 private javax.swing.JScrollPane resultsTableScrollPane;
232 private javax.swing.JPopupMenu rightClickMenu;
233 private javax.swing.JMenuItem selectAllMenuItem;
234 // End of variables declaration//GEN-END:variables
235 private ETable resultsTable;
236
237 private void customizeComponents() {
238 resultsTable.setComponentPopupMenu(rightClickMenu);
239 ActionListener actList = new ActionListener() {
240 @Override
241 public void actionPerformed(ActionEvent e) {
242 JMenuItem jmi = (JMenuItem) e.getSource();
243 if (jmi.equals(copyMenuItem)) {
244 StringBuilder selectedText = new StringBuilder(512);
245 for (int row : resultsTable.getSelectedRows()) {
246 for (int col : resultsTable.getSelectedColumns()) {
247 selectedText.append((String) resultsTable.getValueAt(row, col));
248 selectedText.append('\t');
249 }
250 //if its the last row selected don't add a new line
251 if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
252 selectedText.append(System.lineSeparator());
253 }
254 }
255 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
256 } else if (jmi.equals(selectAllMenuItem)) {
257 resultsTable.selectAll();
258 }
259 }
260 };
261 copyMenuItem.addActionListener(actList);
262
263 selectAllMenuItem.addActionListener(actList);
264 }
265
269 private void resetComponents() {
270
271 ((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
272 }
273
274 @Override
275 public Component getComponent() {
276 return this;
277 }
278
279 @Override
280 public void setArtifact(BlackboardArtifact artifact) {
281 try {
282 ResultsTableArtifact resultsTableArtifact = artifact == null ? null : new ResultsTableArtifact(artifact, artifact.getParent());
283
284 SwingUtilities.invokeLater(new Runnable() {
285 @Override
286 public void run() {
287 updateView(resultsTableArtifact);
288 }
289 });
290
291 } catch (TskCoreException ex) {
292 logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
293 }
294
295 }
296
297 @Override
298 public boolean isSupported(BlackboardArtifact artifact) {
299 // This viewer supports all artifacts.
300 return true;
301 }
302
307 private class ResultsTableArtifact {
308
309 private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
310 private String[][] rowData = null;
311 private final String artifactDisplayName;
312 private final Content content;
313
314 ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
315 artifactDisplayName = artifact.getDisplayName();
316 this.content = content;
317 addRows(artifact);
318 }
319
320 ResultsTableArtifact(String errorMsg) {
321 artifactDisplayName = errorMsg;
322 rowData = new String[1][3];
323 rowData[0] = new String[]{"", errorMsg, ""};
324 content = null;
325 }
326
327 private String[][] getRows() {
328 return rowData;
329 }
330
331 private void addRows(BlackboardArtifact artifact) {
332 List<String[]> rowsToAdd = new ArrayList<>();
333 try {
334 /*
335 * Add rows for each attribute.
336 */
337 for (BlackboardAttribute attr : artifact.getAttributes()) {
338 /*
339 * Attribute value column.
340 */
341 String value;
342 switch (attr.getAttributeType().getValueType()) {
343
344 // Use Autopsy date formatting settings, not TSK defaults
345 case DATETIME:
346 value = TimeZoneUtils.getFormattedTime(attr.getValueLong());
347 break;
348 case JSON:
349 // Get the attribute's JSON value and convert to indented multiline display string
350 String jsonVal = attr.getValueString();
351 JsonElement jsonEl = JsonParser.parseString(jsonVal);
352 value = toJsonDisplayString(jsonEl, "");
353 break;
354
355 case STRING:
356 case INTEGER:
357 case LONG:
358 case DOUBLE:
359 case BYTE:
360 default:
361 value = attr.getDisplayString();
362 break;
363 }
364 /*
365 * Attribute sources column.
366 */
367 String sources = StringUtils.join(attr.getSources(), ", ");
368 rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
369 }
370 /*
371 * Add a row for the source content path.
372 */
373 String path = "";
374 try {
375 if (null != content) {
376 path = content.getUniquePath();
377 }
378 } catch (TskCoreException ex) {
379 logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
380 path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
381 }
382 rowsToAdd.add(new String[]{"Source File Path", path, ""});
383 /*
384 * Add a row for the artifact id.
385 */
386 rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
387 } catch (TskCoreException ex) {
388 rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
389 }
390 rowData = rowsToAdd.toArray(new String[0][0]);
391 }
392
396 String getArtifactDisplayName() {
397 return artifactDisplayName;
398 }
399
400 private static final String INDENT_RIGHT = " ";
401 private static final String NEW_LINE = "\n";
402
412 private String toJsonDisplayString(JsonElement element, String startIndent) {
413 if (element == null || element.isJsonNull()) {
414 return "";
415 } else if (element.isJsonPrimitive()) {
416 return element.getAsString();
417 } else if (element.isJsonObject()) {
418 StringBuilder sb = new StringBuilder("");
419 JsonObject obj = element.getAsJsonObject();
420
421 for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
422 appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb);
423 }
424
425 String returnString = sb.toString();
426 if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
427 returnString = returnString.substring(NEW_LINE.length());
428 }
429 return returnString;
430 } else if (element.isJsonArray()) {
431 StringBuilder sb = new StringBuilder("");
432 JsonArray jsonArray = element.getAsJsonArray();
433 if (jsonArray.size() > 0) {
434 int count = 1;
435 for (JsonElement arrayMember : jsonArray) {
436 sb.append(NEW_LINE).append(String.format("%s%d", startIndent, count));
437 sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT)));
438 count++;
439 }
440 }
441
442 String returnString = sb.toString();
443 if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
444 returnString = returnString.substring(NEW_LINE.length());
445 }
446 return returnString;
447 } else {
448 return "";
449 }
450 }
451
461 private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
462 if (jsonElement.isJsonArray()) {
463 JsonArray jsonArray = jsonElement.getAsJsonArray();
464 if (jsonArray.size() > 0) {
465 int count = 1;
466 sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
467 for (JsonElement arrayMember : jsonArray) {
468 sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
469 sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
470 count++;
471 }
472 }
473 } else if (jsonElement.isJsonObject()) {
474 sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
475 } else if (jsonElement.isJsonPrimitive()) {
476 String attributeName = jsonKey;
477 String attributeValue;
478 if (attributeName.toUpperCase().contains("DATETIME")) {
479 attributeValue = TimeZoneUtils.getFormattedTime(Long.parseLong(jsonElement.getAsString()));
480 } else {
481 attributeValue = jsonElement.getAsString();
482 }
483 sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
484 } else if (jsonElement.isJsonNull()) {
485 sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
486 }
487 }
488 }
489
497 private void updateView(ResultsTableArtifact resultsTableArtifact) {
498 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
499 DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
500 String[][] rows = resultsTableArtifact == null ? new String[0][0] : resultsTableArtifact.getRows();
501 tModel.setDataVector(rows, COLUMN_HEADERS);
504 resultsTable.clearSelection();
505 this.setCursor(null);
506 }
507
511 private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
512
513 @Override
514 public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
515 javax.swing.JTextArea jtex = new javax.swing.JTextArea();
516 if (value instanceof String) {
517 jtex.setText((String) value);
518 jtex.setLineWrap(true);
519 jtex.setWrapStyleWord(false);
520 }
521 //cell backgroud color when selected
522 if (isSelected) {
523 jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
524 } else {
525 jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
526 }
527 return jtex;
528 }
529 }
530}
void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb)
Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)

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