Autopsy  4.19.3
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  */
19 package org.sleuthkit.autopsy.contentviewers.artifactviewers;
20 
21 import java.awt.Component;
22 import java.awt.Cursor;
23 import java.awt.Toolkit;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.awt.datatransfer.StringSelection;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Enumeration;
30 import java.util.List;
31 import java.util.logging.Level;
32 import javax.swing.JMenuItem;
33 import javax.swing.JTextArea;
34 import javax.swing.event.ChangeEvent;
35 import javax.swing.event.ListSelectionEvent;
36 import javax.swing.event.TableColumnModelEvent;
37 import javax.swing.table.DefaultTableModel;
38 import javax.swing.table.TableColumn;
39 import javax.swing.event.TableColumnModelListener;
40 import javax.swing.text.View;
41 import org.apache.commons.lang.StringUtils;
42 import org.openide.util.NbBundle;
44 import org.sleuthkit.datamodel.BlackboardArtifact;
45 import org.sleuthkit.datamodel.BlackboardAttribute;
46 import org.sleuthkit.datamodel.Content;
47 import org.sleuthkit.datamodel.TskCoreException;
48 import org.netbeans.swing.etable.ETable;
49 import com.google.gson.JsonElement;
50 import com.google.gson.JsonObject;
51 import com.google.gson.JsonParser;
52 import com.google.gson.JsonArray;
53 import java.util.Locale;
54 import java.util.Map;
55 import javax.swing.SwingUtilities;
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 
87  initResultsTable();
88  initComponents();
89  resultsTableScrollPane.setViewportView(resultsTable);
90  customizeComponents();
91  resetComponents();
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
205  rightClickMenu.add(copyMenuItem);
206 
207  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultTableArtifactContentViewer.class, "DefaultTableArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
208  rightClickMenu.add(selectAllMenuItem);
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  JsonObject json = JsonParser.parseString(jsonVal).getAsJsonObject();
352 
353  value = toJsonDisplayString(json, "");
354  break;
355 
356  case STRING:
357  case INTEGER:
358  case LONG:
359  case DOUBLE:
360  case BYTE:
361  default:
362  value = attr.getDisplayString();
363  break;
364  }
365  /*
366  * Attribute sources column.
367  */
368  String sources = StringUtils.join(attr.getSources(), ", ");
369  rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
370  }
371  /*
372  * Add a row for the source content path.
373  */
374  String path = "";
375  try {
376  if (null != content) {
377  path = content.getUniquePath();
378  }
379  } catch (TskCoreException ex) {
380  logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
381  path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
382  }
383  rowsToAdd.add(new String[]{"Source File Path", path, ""});
384  /*
385  * Add a row for the artifact id.
386  */
387  rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
388  } catch (TskCoreException ex) {
389  rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
390  }
391  rowData = rowsToAdd.toArray(new String[0][0]);
392  }
393 
397  String getArtifactDisplayName() {
398  return artifactDisplayName;
399  }
400 
401  private static final String INDENT_RIGHT = " ";
402  private static final String NEW_LINE = "\n";
403 
413  private String toJsonDisplayString(JsonElement element, String startIndent) {
414 
415  StringBuilder sb = new StringBuilder("");
416  JsonObject obj = element.getAsJsonObject();
417 
418  for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
419  appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb);
420  }
421 
422  String returnString = sb.toString();
423  if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
424  returnString = returnString.substring(NEW_LINE.length());
425  }
426  return returnString;
427  }
428 
438  private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
439  if (jsonElement.isJsonArray()) {
440  JsonArray jsonArray = jsonElement.getAsJsonArray();
441  if (jsonArray.size() > 0) {
442  int count = 1;
443  sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
444  for (JsonElement arrayMember : jsonArray) {
445  sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
446  sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
447  count++;
448  }
449  }
450  } else if (jsonElement.isJsonObject()) {
451  sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
452  } else if (jsonElement.isJsonPrimitive()) {
453  String attributeName = jsonKey;
454  String attributeValue;
455  if (attributeName.toUpperCase().contains("DATETIME")) {
456  attributeValue = TimeZoneUtils.getFormattedTime(Long.parseLong(jsonElement.getAsString()));
457  } else {
458  attributeValue = jsonElement.getAsString();
459  }
460  sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
461  } else if (jsonElement.isJsonNull()) {
462  sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
463  }
464  }
465  }
466 
474  private void updateView(ResultsTableArtifact resultsTableArtifact) {
475  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
476  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
477  String[][] rows = resultsTableArtifact == null ? new String[0][0] : resultsTableArtifact.getRows();
478  tModel.setDataVector(rows, COLUMN_HEADERS);
479  updateColumnSizes();
480  updateRowHeights();
481  resultsTable.clearSelection();
482  this.setCursor(null);
483  }
484 
488  private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
489 
490  @Override
491  public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
492  javax.swing.JTextArea jtex = new javax.swing.JTextArea();
493  if (value instanceof String) {
494  jtex.setText((String) value);
495  jtex.setLineWrap(true);
496  jtex.setWrapStyleWord(false);
497  }
498  //cell backgroud color when selected
499  if (isSelected) {
500  jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
501  } else {
502  jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
503  }
504  return jtex;
505  }
506  }
507 }
static String getFormattedTime(long epochTime)
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
void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb)

Copyright © 2012-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.