3504: Allow user to format SQLite columns as EPOCH dates

This commit is contained in:
Raman 2018-02-21 13:53:46 -05:00
parent 399c7adfb8
commit 707ec08eff
3 changed files with 134 additions and 52 deletions

View File

@ -1,7 +1,20 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Autopsy Forensic Browser
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers;
@ -17,8 +30,8 @@ import org.openide.nodes.Node;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* Custom Cell renderer to display a SQLite column cell as readable Epoch date/time
* *
* @author raman
*/ */
public class EpochTimeCellRenderer extends DefaultTableCellRenderer { public class EpochTimeCellRenderer extends DefaultTableCellRenderer {
@ -26,40 +39,52 @@ public class EpochTimeCellRenderer extends DefaultTableCellRenderer {
private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName());
private static final String FORMAT_STRING = "yyyy/MM/dd HH:mm:ss"; //NON-NLS private static final String FORMAT_STRING = "yyyy/MM/dd HH:mm:ss"; //NON-NLS
private static final SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT_STRING); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(FORMAT_STRING);
private final boolean renderAsEpoch; private final boolean renderAsEpoch;
public EpochTimeCellRenderer(boolean renderAsEpoch) { EpochTimeCellRenderer(boolean renderAsEpoch) {
this.renderAsEpoch = renderAsEpoch; this.renderAsEpoch = renderAsEpoch;
} }
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null) {
// Set the forceground/background so its obvious when the cell is selected.
String textStr = "No Value"; if (isSelected) {
super.setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
} else {
super.setForeground( table.getForeground());
super.setBackground( table.getBackground());
}
if (value == null) {
setText("");
}
else {
String textStr = "";
try { try {
// get the col property value
if (value instanceof Node.Property<?>) { if (value instanceof Node.Property<?>) {
Node.Property<?> np = (Node.Property)value; Node.Property<?> nodeProp = (Node.Property)value;
textStr = np.getValue().toString(); textStr = nodeProp.getValue().toString();
} }
if (renderAsEpoch) { if (renderAsEpoch) {
long epochTime = Long.parseUnsignedLong(textStr); long epochTime = Long.parseUnsignedLong(textStr);
if (epochTime > 0 ) { if (epochTime > 0 ) {
Font font = getFont();
Font f = getFont(); setFont(font.deriveFont(font.getStyle() | Font.ITALIC));
setFont(f.deriveFont(f.getStyle() | Font.ITALIC)); setText(DATE_FORMAT.format(new Date(epochTime)));
setText(dateFormat.format(new Date(epochTime)));
} }
else { else {
setText(textStr); setText(textStr);
} }
} }
else { else { // Display raw data
setText(textStr); setText(textStr);
} }
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
setText(textStr); setText(textStr);
@ -69,12 +94,12 @@ public class EpochTimeCellRenderer extends DefaultTableCellRenderer {
LOGGER.log(Level.SEVERE, "Error in getting column value.", ex); //NON-NLS LOGGER.log(Level.SEVERE, "Error in getting column value.", ex); //NON-NLS
} }
} }
else {
setText("");
}
return this; return this;
} }
boolean isRenderingAsEpoch() {
return this.renderAsEpoch;
}
} }

View File

@ -18,17 +18,12 @@
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers;
import java.awt.event.ActionEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.netbeans.swing.outline.Outline;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
@ -36,13 +31,15 @@ import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* Factory class to generate nodes for SQLite table rows
*/
public class SQLiteTableRowFactory extends ChildFactory<Integer> { public class SQLiteTableRowFactory extends ChildFactory<Integer> {
private final List<Map<String, Object>> rows; private final List<Map<String, Object>> rows;
//private final Outline outline;
private final List<Action> colActions; private final List<Action> colActions;
public SQLiteTableRowFactory(List<Map<String, Object>> rows, List<Action> actions ) { SQLiteTableRowFactory(List<Map<String, Object>> rows, List<Action> actions ) {
this.rows = rows; this.rows = rows;
this.colActions = actions; this.colActions = actions;
} }
@ -68,13 +65,15 @@ public class SQLiteTableRowFactory extends ChildFactory<Integer> {
} }
/**
*
* Node for SQLite table row
*/
class SQLiteTableRowNode extends AbstractNode { class SQLiteTableRowNode extends AbstractNode {
private final Map<String, Object> row; private final Map<String, Object> row;
//private final Outline outline;
private final List<Action> nodeActions; private final List<Action> nodeActions;
SQLiteTableRowNode(Map<String, Object> row, List<Action> actions) { SQLiteTableRowNode(Map<String, Object> row, List<Action> actions) {
super(Children.LEAF); super(Children.LEAF);
this.row = row; this.row = row;
@ -84,21 +83,20 @@ class SQLiteTableRowNode extends AbstractNode {
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet s = super.createSheet(); Sheet sheet = super.createSheet();
Sheet.Set properties = s.get(Sheet.PROPERTIES); Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
if (properties == null) { if (properties == null) {
properties = Sheet.createPropertiesSet(); properties = Sheet.createPropertiesSet();
s.put(properties); sheet.put(properties);
} }
for (Map.Entry<String, Object> col : row.entrySet()) { for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey(); String colName = col.getKey();
String colVal = col.getValue().toString(); String colVal = col.getValue().toString();
properties.put(new NodeProperty<>(colName, colName, colName, colVal)); // NON-NLS properties.put(new NodeProperty<>(colName, colName, colName, colVal)); // NON-NLS
} }
return s; return sheet;
} }
@Override @Override
@ -106,10 +104,8 @@ class SQLiteTableRowNode extends AbstractNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context))); actions.addAll(Arrays.asList(super.getActions(context)));
actions.addAll(nodeActions); actions.addAll(nodeActions);
//actions.add(parseColAction);
return actions.toArray(new Action[actions.size()]); return actions.toArray(new Action[actions.size()]);
} }

View File

@ -27,6 +27,8 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
@ -41,7 +43,12 @@ import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.openide.util.actions.Presenter;
/**
* Panel to display a SQLite table
*/
class SQLiteTableView extends JPanel implements ExplorerManager.Provider { class SQLiteTableView extends JPanel implements ExplorerManager.Provider {
private final org.openide.explorer.view.OutlineView outlineView; private final org.openide.explorer.view.OutlineView outlineView;
@ -68,6 +75,7 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider {
outline.setRowSelectionAllowed(false); outline.setRowSelectionAllowed(false);
outline.setRootVisible(false); outline.setRootVisible(false);
outline.setCellSelectionEnabled(true);
explorerManager = new ExplorerManager(); explorerManager = new ExplorerManager();
} }
@ -76,6 +84,10 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider {
* *
* @param tableRows * @param tableRows
*/ */
@NbBundle.Messages({"SQLiteTableView.DisplayAs.text=Display as",
"SQLiteTableView.DisplayAsMenuItem.Date=Date",
"SQLiteTableView.DisplayAsMenuItem.RawData=Raw Data"
})
void setupTable(List<Map<String, Object>> tableRows) { void setupTable(List<Map<String, Object>> tableRows) {
@ -110,10 +122,8 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider {
List<Action> nodeActions = new ArrayList<>(); List<Action> nodeActions = new ArrayList<>();
nodeActions.add(new ParseColAction("Parse column as Epoch time", outline, new EpochTimeCellRenderer(true)) ); nodeActions.add(new ParseColAction(Bundle.SQLiteTableView_DisplayAs_text(), outline) );
//nodeActions.add(new ParseColAction("Display column as original type", outline, new DefaultTableCellRenderer()) );
nodeActions.add(new ParseColAction("Display column as original type", outline, new EpochTimeCellRenderer(false)) );
explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows, nodeActions), true))); explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows, nodeActions), true)));
return false; return false;
} }
@ -173,26 +183,77 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider {
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
private class ParseColAction extends AbstractAction { /**
* Action to handle "Display as" menu item.
*
*/
private class ParseColAction extends AbstractAction implements Presenter.Popup {
private final Outline outline; private final Outline outline;
private TableCellRenderer colCellRenderer; private final String displayName;
ParseColAction(String displayName, Outline outline ) {
private ParseColAction(String displayName, Outline outline, TableCellRenderer colCellRenderer ) {
super(displayName); super(displayName);
this.outline = outline; this.outline = outline;
this.colCellRenderer = colCellRenderer; this.displayName = displayName;
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
}
int selCol = outline.getSelectedColumn(); @Override
public JMenuItem getPopupPresenter() {
TableColumnModel columnModel = outline.getColumnModel(); return new DisplayColAsMenu();
}
TableColumn column = columnModel.getColumn(selCol);
column.setCellRenderer(colCellRenderer); /**
* Class to SubMenu for "Display As" menu
*/
private class DisplayColAsMenu extends JMenu {
DisplayColAsMenu() {
super(displayName);
initMenu();
}
final void initMenu() {
int selCol = outline.getSelectedColumn();
if (selCol < 0 ) {
selCol = 1;
}
TableColumnModel columnModel = outline.getColumnModel();
TableColumn column = columnModel.getColumn(selCol);
JMenuItem parseAsEpochItem = new JMenuItem(Bundle.SQLiteTableView_DisplayAsMenuItem_Date());
parseAsEpochItem.addActionListener((ActionEvent evt) -> {
column.setCellRenderer(new EpochTimeCellRenderer(true));
});
parseAsEpochItem.setEnabled(false);
add(parseAsEpochItem);
JMenuItem parseAsOriginalItem = new JMenuItem(Bundle.SQLiteTableView_DisplayAsMenuItem_RawData());
parseAsOriginalItem.addActionListener((ActionEvent evt) -> {
column.setCellRenderer(new EpochTimeCellRenderer(false));
});
parseAsOriginalItem.setEnabled(false);
add(parseAsOriginalItem);
// Enable the relevant menuitem based on the current display state of the column
TableCellRenderer currRenderer = column.getCellRenderer();
if (currRenderer instanceof EpochTimeCellRenderer) {
if (((EpochTimeCellRenderer) currRenderer).isRenderingAsEpoch()) {
parseAsOriginalItem.setEnabled(true);
} else {
parseAsEpochItem.setEnabled(true);
}
}
else {
parseAsEpochItem.setEnabled(true);
}
}
} }
} }
} }