From 09459b3e05b62be7db0dd51e2eb39450db8baae4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Aug 2020 12:27:04 -0400 Subject: [PATCH 01/16] halfway through commenting gui & loading concept --- .../datafetching/DataFetchWorker.java | 147 +++++++++++ .../datafetching/DataLoadingResult.java | 100 ++++++++ .../datafetching/DataProcessor.java | 72 ++++++ .../datafetching/DataProcessorException.java | 43 ++++ .../datafetching/DataResultJTable.java | 232 ++++++++++++++++++ .../datafetching/DataSequentialLoader.java | 49 ++++ .../DefaultPojoListTableDataModel.java | 230 +++++++++++++++++ .../datafetching/PojoListTableDataModel.java | 33 +++ 8 files changed, 906 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java new file mode 100644 index 0000000000..d468de7084 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java @@ -0,0 +1,147 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * A Swing worker data-fetching with result-handler class. + */ +public class DataFetchWorker extends SwingWorker { + + /** + * Holds the functions necessary for a DataFetchWorker. Includes the + * processor and result handler. The args are not included since they are + * likely dynamic. + */ + public static class DataFetchComponents { + + private final DataProcessor processor; + private final Consumer> resultHandler; + + /** + * Main constructor. + * + * @param processor The processor to be used as an argument for the + * DataFetchWorker. + * @param resultHandler The result handler to be used as an argument for + * the DataFetchWorker. + */ + public DataFetchComponents(DataProcessor processor, Consumer> resultHandler) { + this.processor = processor; + this.resultHandler = resultHandler; + } + + /** + * @return The function that processes or fetches the data. + */ + public DataProcessor getProcessor() { + return processor; + } + + /** + * @return When those results are received, this function handles + * presenting the results in the UI. + */ + public Consumer> getResultHandler() { + return resultHandler; + } + } + + private static final Logger logger = Logger.getLogger(DataFetchWorker.class.getName()); + + private final A args; + private final DataProcessor processor; + private final Consumer> resultHandler; + + /** + * Main constructor for this swing worker. + * + * @param components Accepts a components arg which provides a data + * processor and a results consumer. + * @param args The argument to be provided to the data processor. + */ + public DataFetchWorker(DataFetchComponents components, A args) { + this(components.getProcessor(), components.getResultHandler(), args); + } + + /** + * Main constructor for this swing worker. + * + * @param processor The function that will do the processing of the data + * provided the given args. + * @param resultHandler The ui function that will handle the result of the + * data processing. + * @param args The args provided to the data processor. + */ + public DataFetchWorker( + DataProcessor processor, + Consumer> resultHandler, + A args) { + + this.args = args; + this.processor = processor; + this.resultHandler = resultHandler; + } + + @Override + protected R doInBackground() throws Exception { + if (Thread.interrupted() || isCancelled()) { + throw new InterruptedException(); + } + + R result = processor.process(args); + + if (Thread.interrupted() || isCancelled()) { + throw new InterruptedException(); + } + + return result; + } + + @Override + protected void done() { + R result = null; + try { + result = get(); + } catch (InterruptedException ignored) { + // if cancelled, set not loaded andt return + resultHandler.accept(DataLoadingResult.getNotLoaded()); + return; + } catch (ExecutionException ex) { + logger.log(Level.WARNING, "There was an error while fetching results.", ex); + Throwable inner = ex.getCause(); + if (inner != null && inner instanceof DataProcessorException) { + resultHandler.accept(DataLoadingResult.getLoadError((DataProcessorException) inner)); + } + return; + } + + if (Thread.interrupted() || isCancelled()) { + resultHandler.accept(DataLoadingResult.getNotLoaded()); + return; + } + + resultHandler.accept(DataLoadingResult.getLoaded(result)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java new file mode 100644 index 0000000000..da7616f868 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java @@ -0,0 +1,100 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +/** + * The intermediate or end result of a loading process. + */ +class DataLoadingResult { + + // The state of loading in the result. + public enum ProcessorState { + LOADING, NOT_LOADED, LOADED, LOAD_ERROR + } + + // Since loading doesn't have any typed arguments, a static final instance is used. + private static final DataLoadingResult LOADING = new DataLoadingResult(ProcessorState.LOADING, null, null); + + // Since not loaded doesn't have any typed arguments, a static final instance is used. + private static final DataLoadingResult NOT_LOADED = new DataLoadingResult(ProcessorState.LOADED, null, null); + + /** + * @return Returns a data loading result. + */ + public static DataLoadingResult getLoading() { + return (DataLoadingResult) LOADING; + } + + /** + * @return Returns a 'not loaded' result. + */ + public static DataLoadingResult getNotLoaded() { + return (DataLoadingResult) NOT_LOADED; + } + + /** + * Creates a DataLoadingResult of loaded data including the data. + * @param data The data. + * @return The loaded data result. + */ + public static DataLoadingResult getLoaded(R data) { + return new DataLoadingResult(ProcessorState.LOADED, data, null); + } + + /** + * + * @param + * @param e + * @return + */ + static DataLoadingResult getLoadError(DataProcessorException e) { + return new DataLoadingResult(ProcessorState.LOAD_ERROR, null, e); + } + + private final ProcessorState state; + private final R data; + private final DataProcessorException exception; + + private DataLoadingResult(ProcessorState state, R data, DataProcessorException exception) { + this.state = state; + this.data = data; + this.exception = exception; + } + + /** + * @return The current loading state. + */ + public ProcessorState getState() { + return state; + } + + /** + * @return The data if the state is LOADED. + */ + public R getData() { + return data; + } + + /** + * @return The exception if the state is LOAD_ERROR. + */ + public DataProcessorException getException() { + return exception; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java new file mode 100644 index 0000000000..4751c63f87 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java @@ -0,0 +1,72 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +/** + * A function that accepts input of type I and outputs type O. This function is + * meant to be utilized with DataFetchWorker and can therefore, throw an + * interrupted exception if the processing is cancelled or a + * DataProcessorException in the event that the processing encountered an error. + */ +@FunctionalInterface +public interface DataProcessor { + + /** + * Wraps a DataProcessor in statements looking to see if the thread has been + * interrupted and throws an InterruptedException in that event. + * + * @param toBeWrapped The data processor to be wrapped. + * + * @return The wrapped data processor that will throw an interrupted + * exception before or after the toBeWrapped data processor has been + * run. + */ + public static DataProcessor wrap(DataProcessor toBeWrapped) { + return new DataProcessor() { + @Override + public O1 process(I1 input) throws InterruptedException, DataProcessorException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + + O1 output = toBeWrapped.process(input); + if (Thread.interrupted()) { + throw new InterruptedException(); + } + + return output; + } + }; + } + + /** + * A function that accepts an input argument and outputs a result. Since it + * is meant to be used with the DataFetchWorker, it throws an interrupted + * exception if the thread has been interrupted and data processing + * exception if there is an error during processing. + * + * @param input The input argument. + * + * @return The output result. + * + * @throws InterruptedException + * @throws DataProcessorException + */ + O process(I input) throws InterruptedException, DataProcessorException; +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java new file mode 100644 index 0000000000..ae659cfbfb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java @@ -0,0 +1,43 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +/** + * An Exception that is thrown when there is an issue processing data in a + * DataProcessor. + */ +public class DataProcessorException extends Exception { + + /** + * Main constructor. + * @param string The error message. + */ + public DataProcessorException(String string) { + super(string); + } + + /** + * Main constructor. + * @param string The error message. + * @param thrwbl The inner exception. + */ + public DataProcessorException(String string, Throwable thrwbl) { + super(string, thrwbl); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java new file mode 100644 index 0000000000..627dfb273a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java @@ -0,0 +1,232 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JLayer; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.plaf.LayerUI; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * A JTable that displays a list of POJO items and also can display messages for + * loading, load error, and not loaded. + */ +@Messages({ + "DataResultJTable_loadingMessage_defaultText=Loading results...", + "DataResultJTable_errorMessage_defaultText=There was an error loading results." +}) +class DataResultJTable extends JPanel { + + /** + * JTables don't allow display messages. So this LayerUI is used to display + * the contents of a child JLabel. Inspired by TableWaitLayerTest (Animating + * a Busy Indicator): + * https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html. + */ + private static class Overlay extends LayerUI { + + private final JLabel child; + private boolean visible; + + Overlay() { + child = new JLabel(); + child.setHorizontalAlignment(JLabel.CENTER); + child.setVerticalAlignment(JLabel.CENTER); + child.setOpaque(false); + + } + + /** + * @return Whether or not this message overlay should be visible. + */ + boolean isVisible() { + return visible; + } + + /** + * Sets this layer visible when painted. In order to be shown in UI, + * this component needs to be repainted. + * + * @param visible Whether or not it is visible. + */ + void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * @return The child JLabel component. + */ + JLabel getChild() { + return child; + } + + /** + * Sets the message to be displayed in the child jlabel. + * @param message The message to be displayed. + */ + void setMessage(String message) { + child.setText(String.format("
%s
", message)); + } + + @Override + public void paint(Graphics g, JComponent c) { + int w = c.getWidth(); + int h = c.getHeight(); + + // Paint the underlying view. + super.paint(g, c); + + if (!visible) { + return; + } + + // paint the jlabel if visible. + child.setBounds(0, 0, w, h); + child.paint(g); + } + } + + private static final Logger logger = Logger.getLogger(DataResultJTable.class.getName()); + + private static final String DEFAULT_LOADING_MESSAGE = Bundle.DataResultJTable_loadingMessage_defaultText(); + private static final String DEFAULT_ERROR_MESSAGE = Bundle.DataResultJTable_errorMessage_defaultText(); + private static final String DEFAULT_NO_RESULTS_MESSAGE = ""; + private static final String DEFAULT_NOT_LOADED_MESSAGE = ""; + + private final JTable table; + private final JScrollPane tableScrollPane; + private final JLayer dualLayer; + private final Overlay overlayLayer; + + private String loadingMessage = DEFAULT_LOADING_MESSAGE; + private String errorMessage = DEFAULT_ERROR_MESSAGE; + private String noResultsMessage = DEFAULT_NO_RESULTS_MESSAGE; + private String notLoadedMessage = DEFAULT_NOT_LOADED_MESSAGE; + + private PojoListTableDataModel tableModel = null; + + public DataResultJTable(PojoListTableDataModel tableModel) { + this.tableModel = tableModel; + this.table = new JTable(tableModel, tableModel.getTableColumnModel()); + this.table.getTableHeader().setReorderingAllowed(false); + + this.overlayLayer = new Overlay(); + this.tableScrollPane = new JScrollPane(table); + this.dualLayer = new JLayer(tableScrollPane, overlayLayer); + setLayout(new BorderLayout()); + add(dualLayer, BorderLayout.CENTER); + } + + String getLoadingMessage() { + return loadingMessage; + } + + String getErrorMessage() { + return errorMessage; + } + + String getNoResultsMessage() { + return noResultsMessage; + } + + String getNotLoadedMessage() { + return notLoadedMessage; + } + + public DataResultJTable setLoadingMessage(String loadingMessage) { + this.loadingMessage = loadingMessage; + return this; + } + + public DataResultJTable setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + public DataResultJTable setNoResultsMessage(String noResultsMessage) { + this.noResultsMessage = noResultsMessage; + return this; + } + + public DataResultJTable setNotLoadedMessage(String notLoadedMessage) { + this.notLoadedMessage = notLoadedMessage; + return this; + } + + private void setResultList(List data) { + List dataToSet = (data != null) + ? Collections.unmodifiableList(data) + : Collections.emptyList(); + + tableScrollPane.getVerticalScrollBar().setValue(0); + this.tableModel.setDataRows(dataToSet); + repaint(); + } + + private void setOverlay(boolean visible, String message) { + this.overlayLayer.setVisible(visible); + this.overlayLayer.setMessage(message); + repaint(); + } + + public void setResult(DataLoadingResult> result) { + if (result == null) { + logger.log(Level.SEVERE, "Null data processor result received."); + return; + } + + switch (result.getState()) { + case LOADED: + if (result.getData() == null || result.getData().size() <= 0) { + setResultList(null); + setOverlay(true, getNoResultsMessage()); + } else { + setResultList(result.getData()); + setOverlay(false, null); + + } + break; + case NOT_LOADED: + setResultList(null); + setOverlay(true, getNotLoadedMessage()); + break; + case LOADING: + setResultList(null); + setOverlay(true, getLoadingMessage()); + break; + case LOAD_ERROR: + setResultList(null); + setOverlay(true, getErrorMessage()); + break; + default: + logger.log(Level.SEVERE, "No known loading state was found in result."); + break; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java new file mode 100644 index 0000000000..73a4f18646 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java @@ -0,0 +1,49 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +// taken from https://stackoverflow.com/questions/31580805/java-swingworker-one-after-another-and-update-gui +class DataSequentialLoader { + private final ExecutorService executorService = Executors.newFixedThreadPool(1); + private List> workers = Collections.emptyList(); + private List> futures = Collections.emptyList(); + + DataSequentialLoader() {} + + synchronized void resetLoad(List> submittedWorkers) { + cancelRunning(); + this.workers = Collections.unmodifiableList(submittedWorkers); + this.futures = this.workers.stream() + .map((w) -> executorService.submit(w)) + .collect(Collectors.toList()); + } + + synchronized void cancelRunning() { + futures.forEach((f) -> f.cancel(true)); + workers = Collections.emptyList(); + futures = Collections.emptyList(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java new file mode 100644 index 0000000000..edce0c2fdc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java @@ -0,0 +1,230 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +import java.awt.Component; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import org.apache.commons.lang3.StringUtils; + +/** + * + * @author gregd + */ +class DefaultPojoListTableDataModel extends AbstractTableModel implements PojoListTableDataModel { + enum HorizontalAlign { LEFT, CENTER, RIGHT } + + interface CellModel { + String getText(); + String getTooltip(); + } + + static class DefaultCellModel implements CellModel { + private final String text; + private String tooltip; + + public DefaultCellModel(String text) { + this.text = text; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getTooltip() { + return tooltip; + } + + public DefaultCellModel setTooltip(String tooltip) { + this.tooltip = tooltip; + return this; + } + + @Override + public String toString() { + return getText(); + } + } + + interface ColumnModel { + String getTitle(); + Function getCellCreator(); + Integer getWidth(); + HorizontalAlign getCellHorizontalAlignment(); + } + + static class DefaultColumnModel implements ColumnModel { + private final String title; + private final Function cellCreator; + private Integer width; + private HorizontalAlign cellHorizontalAlignment; + + public DefaultColumnModel(String title, Function retriever) { + this((obj) -> new DefaultCellModel(retriever.apply(obj)), title); + } + + public DefaultColumnModel(Function retriever, String title) { + this.title = title; + this.cellCreator = retriever; + } + + public String getTitle() { + return title; + } + + public Function getCellCreator() { + return cellCreator; + } + + public Integer getWidth() { + return width; + } + + public HorizontalAlign getCellHorizontalAlignment() { + return cellHorizontalAlignment; + } + + public DefaultColumnModel setWidth(Integer width) { + this.width = width; + return this; + } + + public DefaultColumnModel setCellHorizontalAlignment(HorizontalAlign cellHorizontalAlignment) { + this.cellHorizontalAlignment = cellHorizontalAlignment; + return this; + } + } + + private final List> columns; + private List dataRows = Collections.emptyList(); + + DefaultPojoListTableDataModel(List> columns) { + this.columns = Collections.unmodifiableList(columns); + } + + @Override + public TableColumnModel getTableColumnModel() { + TableColumnModel toRet = new DefaultTableColumnModel(); + for (int i = 0; i < columns.size(); i++) { + TableColumn col = new TableColumn(i); + ColumnModel model = columns.get(i); + if (model.getWidth() != null && model.getWidth() >= 0) { + col.setWidth(model.getWidth()); + } + + col.setHeaderValue(model.getTitle()); + + col.setCellRenderer(new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent( + JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (value instanceof CellModel) { + return DefaultPojoListTableDataModel.this.getTableCellRendererComponent(c, columns.get(column), (CellModel) value); + } else { + return c; + } + } + }); + toRet.addColumn(col); + } + return toRet; + } + + + protected Component getTableCellRendererComponent(JLabel defaultCell, ColumnModel columnModel, CellModel cellModel) { + String text = cellModel.getText(); + if (StringUtils.isNotBlank(text)) { + defaultCell.setText(text); + } + + String tooltip = cellModel.getTooltip(); + if (StringUtils.isNotBlank(tooltip)) { + defaultCell.setToolTipText(tooltip); + } + + if (columnModel.getCellHorizontalAlignment() != null) { + switch (columnModel.getCellHorizontalAlignment()) { + case LEFT: + defaultCell.setHorizontalAlignment(JLabel.LEFT); + break; + case CENTER: + defaultCell.setHorizontalAlignment(JLabel.CENTER); + break; + case RIGHT: + defaultCell.setHorizontalAlignment(JLabel.RIGHT); + break; + } + } + + return defaultCell; + } + + @Override + public List getDataRows() { + return dataRows; + } + + @Override + public void setDataRows(List dataRows) { + this.dataRows = dataRows == null ? Collections.emptyList() : Collections.unmodifiableList(dataRows); + super.fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return dataRows.size(); + } + + @Override + public int getColumnCount() { + return columns.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (rowIndex < 0 || rowIndex >= dataRows.size() || columnIndex < 0 || columnIndex >= columns.size()) { + return null; + } + + return columns.get(columnIndex).getCellCreator().apply(dataRows.get(rowIndex)); + } + + @Override + public String getColumnName(int column) { + if (column < 0 || column >= columns.size()) { + return null; + } + + return columns.get(column).getTitle(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java new file mode 100644 index 0000000000..66fc9537cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java @@ -0,0 +1,33 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datafetching; + +import java.util.List; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; + +/** + * + * @author gregd + */ +public interface PojoListTableDataModel extends TableModel { + TableColumnModel getTableColumnModel(); + List getDataRows(); + void setDataRows(List dataRows); +} From d90c0faf3ceb159f1ac049518a22454a0a09dd18 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Aug 2020 15:00:08 -0400 Subject: [PATCH 02/16] formatting and commenting --- .../internal}/DataFetchWorker.java | 2 +- .../internal}/DataLoadingResult.java | 2 +- .../internal}/DataProcessor.java | 2 +- .../internal}/DataProcessorException.java | 2 +- .../internal}/DataResultJTable.java | 55 ++++++- .../DefaultPojoListTableDataModel.java | 146 ++++++++++++++++-- .../internal}/PojoListTableDataModel.java | 23 ++- .../SwingWorkerSequentialRunner.java} | 35 ++++- 8 files changed, 229 insertions(+), 38 deletions(-) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DataFetchWorker.java (98%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DataLoadingResult.java (97%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DataProcessor.java (97%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DataProcessorException.java (95%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DataResultJTable.java (82%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/DefaultPojoListTableDataModel.java (62%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching => guiutils/internal}/PojoListTableDataModel.java (61%) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datafetching/DataSequentialLoader.java => guiutils/internal/SwingWorkerSequentialRunner.java} (61%) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java similarity index 98% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java index d468de7084..9fa7288daa 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java index da7616f868..5731aac902 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; /** * The intermediate or end result of a loading process. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java index 4751c63f87..f99f33c39a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; /** * A function that accepts input of type I and outputs type O. This function is diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java similarity index 95% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java index ae659cfbfb..cc14647a06 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataProcessorException.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; /** * An Exception that is thrown when there is an issue processing data in a diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java index 627dfb273a..cf747b5259 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataResultJTable.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; import java.awt.BorderLayout; import java.awt.Graphics; @@ -41,7 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; "DataResultJTable_loadingMessage_defaultText=Loading results...", "DataResultJTable_errorMessage_defaultText=There was an error loading results." }) -class DataResultJTable extends JPanel { +public class DataResultJTable extends JPanel { /** * JTables don't allow display messages. So this LayerUI is used to display @@ -131,6 +131,10 @@ class DataResultJTable extends JPanel { private PojoListTableDataModel tableModel = null; + /** + * Main constructor. + * @param tableModel The model to use for the table. + */ public DataResultJTable(PojoListTableDataModel tableModel) { this.tableModel = tableModel; this.table = new JTable(tableModel, tableModel.getTableColumnModel()); @@ -143,37 +147,69 @@ class DataResultJTable extends JPanel { add(dualLayer, BorderLayout.CENTER); } - String getLoadingMessage() { + /** + * @return The message shown when loading. + */ + public String getLoadingMessage() { return loadingMessage; } + /** + * @return The message shown when there is an exception. + */ String getErrorMessage() { return errorMessage; } + /** + * @return The message shown when there are no results. + */ String getNoResultsMessage() { return noResultsMessage; } + /** + * @return The message shown when the table has not been loaded. + */ String getNotLoadedMessage() { return notLoadedMessage; } + /** + * Sets the loading message. + * @param loadingMessage The loading message. + * @return As a utility, returns this. + */ public DataResultJTable setLoadingMessage(String loadingMessage) { this.loadingMessage = loadingMessage; return this; } + /** + * Sets the error message + * @param errorMessage The error message. + * @return As a utility, returns this. + */ public DataResultJTable setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; return this; } + /** + * Sets the message to be shown when no results are present. + * @param noResultsMessage The 'no results' message. + * @return As a utility, returns this. + */ public DataResultJTable setNoResultsMessage(String noResultsMessage) { this.noResultsMessage = noResultsMessage; return this; } + /** + * Sets the 'not loaded' message. + * @param notLoadedMessage The message to be shown when results are not loaded. + * @return As a utility, returns this. + */ public DataResultJTable setNotLoadedMessage(String notLoadedMessage) { this.notLoadedMessage = notLoadedMessage; return this; @@ -195,10 +231,15 @@ class DataResultJTable extends JPanel { repaint(); } - public void setResult(DataLoadingResult> result) { + /** + * Sets the result to be displayed. + * @param result The loading result to be displayed. + * @return As a utility, returns this. + */ + public DataResultJTable setResult(DataLoadingResult> result) { if (result == null) { logger.log(Level.SEVERE, "Null data processor result received."); - return; + return this; } switch (result.getState()) { @@ -209,7 +250,6 @@ class DataResultJTable extends JPanel { } else { setResultList(result.getData()); setOverlay(false, null); - } break; case NOT_LOADED: @@ -221,6 +261,7 @@ class DataResultJTable extends JPanel { setOverlay(true, getLoadingMessage()); break; case LOAD_ERROR: + logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException()); setResultList(null); setOverlay(true, getErrorMessage()); break; @@ -228,5 +269,7 @@ class DataResultJTable extends JPanel { logger.log(Level.SEVERE, "No known loading state was found in result."); break; } + + return this; } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java similarity index 62% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java index edce0c2fdc..8671877459 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DefaultPojoListTableDataModel.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; import java.awt.Component; import java.util.Collections; @@ -32,21 +32,47 @@ import javax.swing.table.TableColumnModel; import org.apache.commons.lang3.StringUtils; /** - * - * @author gregd + * The default implementation of a table model for the DataResultJTable. This + * class provides a TableModel and TableColumnModel to be used with that class. */ -class DefaultPojoListTableDataModel extends AbstractTableModel implements PojoListTableDataModel { - enum HorizontalAlign { LEFT, CENTER, RIGHT } - - interface CellModel { +public class DefaultPojoListTableDataModel extends AbstractTableModel implements PojoListTableDataModel { + + /** + * Describes the horizontal alignment. + */ + public enum HorizontalAlign { + LEFT, CENTER, RIGHT + } + + /** + * Basic interface for a cell model. + */ + public interface CellModel { + + /** + * @return The text to be shown in the cell. + */ String getText(); + + /** + * @return The tooltip (if any) to be displayed in the cell. + */ String getTooltip(); } - static class DefaultCellModel implements CellModel { + /** + * The default cell model. + */ + public static class DefaultCellModel implements CellModel { + private final String text; private String tooltip; + /** + * Main constructor. + * + * @param text The text to be displayed in the cell. + */ public DefaultCellModel(String text) { this.text = text; } @@ -60,7 +86,14 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj public String getTooltip() { return tooltip; } - + + /** + * Sets the tooltip for this cell model. + * + * @param tooltip The tooltip for the cell model. + * + * @return As a utility, returns this. + */ public DefaultCellModel setTooltip(String tooltip) { this.tooltip = tooltip; return this; @@ -72,49 +105,119 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj } } - interface ColumnModel { + /** + * The column model for the table. + */ + public interface ColumnModel { + + /** + * @return The column header title. + */ String getTitle(); + + /** + * @return A function to generate the contents for the cell based on the + * data in the POJO. + */ Function getCellCreator(); + + /** + * @return The width of the column to provide to the JTable. This can be + * left as null. + */ Integer getWidth(); + + /** + * @return The horizontal alignment for the text in the cell. + */ HorizontalAlign getCellHorizontalAlignment(); } - static class DefaultColumnModel implements ColumnModel { + /** + * The default implementation for the column model. + */ + public static class DefaultColumnModel implements ColumnModel { + private final String title; private final Function cellCreator; private Integer width; private HorizontalAlign cellHorizontalAlignment; - + + /** + * Main constructor. + * + * @param title The title for the column header. + * @param retriever Retrieves the value for cell from the POJO provided + * in the row. + */ public DefaultColumnModel(String title, Function retriever) { this((obj) -> new DefaultCellModel(retriever.apply(obj)), title); } + /** + * Main constructor. + * + * @param retriever Generates a cell model based on the POJO provided + * for the row. + * @param title The title for the column header. + */ public DefaultColumnModel(Function retriever, String title) { this.title = title; this.cellCreator = retriever; } + /** + * @return The title for the column header. + */ public String getTitle() { return title; } + /** + * @return The means of converting the POJO for a row into a cell model + * for this column. + */ public Function getCellCreator() { return cellCreator; } + /** + * @return The width (if any) to specify for this column in the jtable. + */ public Integer getWidth() { return width; } + /** + * @return The horizontal alignment of the text in the cells if any. + */ public HorizontalAlign getCellHorizontalAlignment() { return cellHorizontalAlignment; } + /** + * Sets the width of the column to provide to the jtable. This method + * should be called prior to being provided as an argument to the + * DefaultPojoListTableDataModel. + * + * @param width The width of the column. + * + * @return As a utility, returns this. + */ public DefaultColumnModel setWidth(Integer width) { this.width = width; return this; } + /** + * Sets the cell horizontal alignment for the cells in this column. This + * method should be called prior to being provided as an argument to the + * DefaultPojoListTableDataModel. + * + * @param cellHorizontalAlignment The alignment of text in the cell. + * + * @return As a utility, returns this. + */ public DefaultColumnModel setCellHorizontalAlignment(HorizontalAlign cellHorizontalAlignment) { this.cellHorizontalAlignment = cellHorizontalAlignment; return this; @@ -124,7 +227,12 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj private final List> columns; private List dataRows = Collections.emptyList(); - DefaultPojoListTableDataModel(List> columns) { + /** + * Main constructor + * + * @param columns The model for the columns in this table. + */ + public DefaultPojoListTableDataModel(List> columns) { this.columns = Collections.unmodifiableList(columns); } @@ -137,7 +245,7 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj if (model.getWidth() != null && model.getWidth() >= 0) { col.setWidth(model.getWidth()); } - + col.setHeaderValue(model.getTitle()); col.setCellRenderer(new DefaultTableCellRenderer() { @@ -159,7 +267,13 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj return toRet; } - + /** + * Customizes the jlabel to match the column model and cell model provided. + * @param defaultCell The cell to customize that will be displayed in the jtable. + * @param columnModel The column model for this cell. + * @param cellModel The cell model for this cell. + * @return The provided defaultCell. + */ protected Component getTableCellRendererComponent(JLabel defaultCell, ColumnModel columnModel, CellModel cellModel) { String text = cellModel.getText(); if (StringUtils.isNotBlank(text)) { @@ -170,7 +284,7 @@ class DefaultPojoListTableDataModel extends AbstractTableModel implements Poj if (StringUtils.isNotBlank(tooltip)) { defaultCell.setToolTipText(tooltip); } - + if (columnModel.getCellHorizontalAlignment() != null) { switch (columnModel.getCellHorizontalAlignment()) { case LEFT: diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java similarity index 61% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java index 66fc9537cf..0f05e5e835 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/PojoListTableDataModel.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java @@ -16,18 +16,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; import java.util.List; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; /** - * - * @author gregd + * An interface to be used with the DataResultJTable that specifies a TableModel + * and TableColumnModel to be used with a JTable based on a list of object type + * T. */ public interface PojoListTableDataModel extends TableModel { + + /** + * @return The TableColumnModel to be used with the jtable. + */ TableColumnModel getTableColumnModel(); + + /** + * @return The list of objects supporting the rows to be displayed in the + * table. + */ List getDataRows(); - void setDataRows(List dataRows); + + /** + * Sets the list of objects to be displayed in the table. + * @param dataRows The datarows to be displayed. + */ + void setDataRows(List dataRows); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java similarity index 61% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java rename to Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java index 73a4f18646..215b9dc228 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datafetching/DataSequentialLoader.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datafetching; +package org.sleuthkit.autopsy.guiutils.internal; import java.util.Collections; import java.util.List; @@ -24,24 +24,43 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Collectors; +import javax.swing.SwingWorker; + +/** + * Runs a list of swing workers in sequential order. Also, provides the ability + * to reset or cancel a run. + * + * Based on: + * https://stackoverflow.com/questions/31580805/java-swingworker-one-after-another-and-update-gui + */ +public class SwingWorkerSequentialRunner { -// taken from https://stackoverflow.com/questions/31580805/java-swingworker-one-after-another-and-update-gui -class DataSequentialLoader { private final ExecutorService executorService = Executors.newFixedThreadPool(1); - private List> workers = Collections.emptyList(); + private List> workers = Collections.emptyList(); private List> futures = Collections.emptyList(); - DataSequentialLoader() {} - - synchronized void resetLoad(List> submittedWorkers) { + /** + * Cancels currently running operations and starts running the new list of + * swing workers. + * + * @param submittedWorkers The list of submitted swing workers. + */ + public synchronized void resetLoad(List> submittedWorkers) { cancelRunning(); + if (submittedWorkers == null) { + return; + } + this.workers = Collections.unmodifiableList(submittedWorkers); this.futures = this.workers.stream() .map((w) -> executorService.submit(w)) .collect(Collectors.toList()); } - synchronized void cancelRunning() { + /** + * Cancels currently running items. + */ + public synchronized void cancelRunning() { futures.forEach((f) -> f.cancel(true)); workers = Collections.emptyList(); futures = Collections.emptyList(); From e0351c7c0a64572de88185f47db55bc0e6c528b3 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Aug 2020 07:13:46 -0400 Subject: [PATCH 03/16] additional comment --- .../autopsy/guiutils/internal/DataLoadingResult.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java index 5731aac902..f341b374a3 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java @@ -58,9 +58,8 @@ class DataLoadingResult { } /** - * - * @param - * @param e + * Returns a load error result. + * @param e The exception (if any) present with the error. * @return */ static DataLoadingResult getLoadError(DataProcessorException e) { From 78d69f173b83f3682c30f86dca01b44aea3ad01f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Aug 2020 11:26:24 -0400 Subject: [PATCH 04/16] bug fixes --- .../datamodel/DataSourceInfoUtilities.java | 19 +- .../DataSourceTopDomainsSummary.java | 61 ++++ .../DataSourceTopProgramsSummary.java | 79 +++-- .../datamodel/SleuthkitCaseProvider.java | 33 ++ .../datamodel/TopDomainsResult.java | 57 +++ .../datasourcesummary/ui/Bundle.properties | 3 +- .../ui/Bundle.properties-MERGED | 6 +- .../DataSourceSummaryUserActivityPanel.form | 206 ++++++++--- .../DataSourceSummaryUserActivityPanel.java | 330 +++++++----------- .../internal/Bundle.properties-MERGED | 2 + .../guiutils/internal/DataFetchWorker.java | 19 +- .../guiutils/internal/DataLoadingResult.java | 2 +- .../DefaultPojoListTableDataModel.java | 8 +- 13 files changed, 543 insertions(+), 282 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java create mode 100644 Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index b72bc14330..1f5f6e362f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -108,7 +108,24 @@ final class DataSourceInfoUtilities { * obtained. */ static T getBaseQueryResult(String query, ResultSetHandler processor, String errorMessage) { - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) { + return getBaseQueryResult(SleuthkitCaseProvider.DEFAULT, query, processor, errorMessage); + } + + + /** + * Retrieves a result based on the provided query. + * + * @param provider The means of obtaining a SleuthkitCase. + * @param query The query. + * @param processor The result set handler. + * @param errorMessage The error message to display if there is an error + * retrieving the resultset. + * + * @return The ResultSetHandler value or null if no ResultSet could be + * obtained. + */ + static T getBaseQueryResult(SleuthkitCaseProvider provider, String query, ResultSetHandler processor, String errorMessage) { + try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); try { return processor.process(resultSet); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java new file mode 100644 index 0000000000..d61a21e307 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -0,0 +1,61 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datamodel; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.sleuthkit.datamodel.DataSource; + +/** + * Provides summary information about top domains in a datasource. + */ +public class DataSourceTopDomainsSummary { + private static final long SLEEP_TIME = 5000; + +// private final SleuthkitCaseProvider provider; +// +// public DataSourceTopDomainsSummary() { +// this(SleuthkitCaseProvider.DEFAULT); +// } +// +// public DataSourceTopDomainsSummary(SleuthkitCaseProvider provider) { +// this.provider = provider; +// } + + interface Function2 { + O apply(A1 a1, A2 a2); + } + + public List getRecentDomains(DataSource dataSource, int count) throws InterruptedException { + Thread.sleep(SLEEP_TIME); + final String dId = Long.toString(dataSource.getId()); + final Function2 getId = (s,idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx); + return IntStream.range(0, count) + .mapToObj(num -> new TopDomainsResult( + getId.apply("domain", num), + getId.apply("url", num), + (long)num, + new Date(120, 1, num) + )) + .collect(Collectors.toList()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java index 7c6927cd0d..8bc527f2bb 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java @@ -66,6 +66,36 @@ public class DataSourceTopProgramsSummary { */ private static final String QUERY_SUFFIX = "_query"; + /** + * Functions that determine the folder name of a list of path elements. If + * not matched, function returns null. + */ + private static final List, String>> SHORT_FOLDER_MATCHERS = Arrays.asList( + // handle Program Files and Program Files (x86) - if true, return the next folder + (pathList) -> { + if (pathList.size() < 2) { + return null; + } + + String rootParent = pathList.get(0).toUpperCase(); + if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) { + return pathList.get(1); + } else { + return null; + } + }, + // if there is a folder named "APPLICATION DATA" or "APPDATA" + (pathList) -> { + for (String pathEl : pathList) { + String uppered = pathEl.toUpperCase(); + if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) { + return "AppData"; + } + } + return null; + } + ); + /** * Creates a sql statement querying the blackboard attributes table for a * particular attribute type and returning a specified value. That query @@ -138,6 +168,17 @@ public class DataSourceTopProgramsSummary { private static String getLikeClause(String column, String likeString, boolean isLike) { return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'"; } + + + private final SleuthkitCaseProvider provider; + + public DataSourceTopProgramsSummary() { + this(SleuthkitCaseProvider.DEFAULT); + } + + public DataSourceTopProgramsSummary(SleuthkitCaseProvider provider) { + this.provider = provider; + } /** * Retrieves a list of the top programs used on the data source. Currently @@ -149,7 +190,7 @@ public class DataSourceTopProgramsSummary { * * @return */ - public static List getTopPrograms(DataSource dataSource, int count) { + public List getTopPrograms(DataSource dataSource, int count) { if (dataSource == null || count <= 0) { return Collections.emptyList(); } @@ -225,38 +266,9 @@ public class DataSourceTopProgramsSummary { return progResults; }; - return getBaseQueryResult(query, handler, errorMessage); + return getBaseQueryResult(provider, query, handler, errorMessage); } - - /** - * Functions that determine the folder name of a list of path elements. If - * not matched, function returns null. - */ - private static final List, String>> SHORT_FOLDER_MATCHERS = Arrays.asList( - // handle Program Files and Program Files (x86) - if true, return the next folder - (pathList) -> { - if (pathList.size() < 2) { - return null; - } - - String rootParent = pathList.get(0).toUpperCase(); - if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) { - return pathList.get(1); - } else { - return null; - } - }, - // if there is a folder named "APPLICATION DATA" or "APPDATA" - (pathList) -> { - for (String pathEl : pathList) { - String uppered = pathEl.toUpperCase(); - if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) { - return "AppData"; - } - } - return null; - } - ); + /** * Determines a short folder name if any. Otherwise, returns empty string. @@ -289,7 +301,4 @@ public class DataSourceTopProgramsSummary { return ""; } - - private DataSourceTopProgramsSummary() { - } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java new file mode 100644 index 0000000000..9855fde185 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java @@ -0,0 +1,33 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datamodel; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.SleuthkitCase; + +/** + * An interface to provide the current SleuthkitCase object. By default, this + * uses Case.getCurrentCaseThrows().getSleuthkkitCase(). + */ +public interface SleuthkitCaseProvider { + public static final SleuthkitCaseProvider DEFAULT = () -> Case.getCurrentCaseThrows().getSleuthkitCase(); + + SleuthkitCase get() throws NoCurrentCaseException; +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java new file mode 100644 index 0000000000..4d8bc1c012 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java @@ -0,0 +1,57 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.datamodel; + +import java.util.Date; + +/** + * Describes a result of a program run on a datasource. + */ +public class TopDomainsResult { + + private final String domain; + private final String url; + private final Long visitTimes; + private final Date lastVisit; + + public TopDomainsResult(String domain, String url, Long visitTimes, Date lastVisit) { + this.domain = domain; + this.url = url; + this.visitTimes = visitTimes; + this.lastVisit = lastVisit; + } + + public String getDomain() { + return domain; + } + + public String getUrl() { + return url; + } + + public Long getVisitTimes() { + return visitTimes; + } + + public Date getLastVisit() { + return lastVisit; + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index 462d910dde..708010e07a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -35,4 +35,5 @@ DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type -DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs +DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 811040a3ca..1d72d08d52 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -75,8 +75,12 @@ DataSourceSummaryTabbedPane_countsTab_title=Counts DataSourceSummaryTabbedPane_detailsTab_title=Details DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_userActivityTab_title=User Activity -DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs +DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains DataSourceSummaryUserActivityPanel_tab_title=User Activity +DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain +DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access +DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form index 0829b9e045..4b19d7b0e3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form @@ -1,6 +1,11 @@
+ + + + + @@ -11,59 +16,180 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + + + + - - - - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index 21b2a30f22..29e74b66b8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -21,20 +21,26 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.awt.Component; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.Locale; -import javax.swing.JLabel; -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; +import java.util.stream.Collectors; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopDomainsSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult; +import org.sleuthkit.autopsy.guiutils.internal.DataFetchWorker; +import org.sleuthkit.autopsy.guiutils.internal.DataFetchWorker.DataFetchComponents; +import org.sleuthkit.autopsy.guiutils.internal.DataLoadingResult; +import org.sleuthkit.autopsy.guiutils.internal.DataResultJTable; +import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel; +import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.DefaultCellModel; +import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.DefaultColumnModel; +import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.HorizontalAlign; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.autopsy.guiutils.internal.SwingWorkerSequentialRunner; /** * A panel to display user activity. @@ -44,18 +50,21 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times", - "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run" -}) + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run", + "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain", + "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", + "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access",}) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); private static final int TOP_PROGS_COUNT = 10; - private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); + private static final int TOP_DOMAINS_COUNT = 10; - static { - RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); - } + private final SwingWorkerSequentialRunner loader = new SwingWorkerSequentialRunner(); + private final DataResultJTable topProgramsTable; + private final DataResultJTable recentDomainsTable; + private final List> dataFetchComponents; private DataSource dataSource; @@ -63,8 +72,61 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * Creates new form DataSourceUserActivityPanel */ public DataSourceSummaryUserActivityPanel() { + this(new DataSourceTopProgramsSummary(), new DataSourceTopDomainsSummary()); + } + + public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) { + // set up recent programs table + this.topProgramsTable = new DataResultJTable<>(new DefaultPojoListTableDataModel<>(Arrays.asList( + new DefaultColumnModel( + (prog) -> new DefaultCellModel(prog.getProgramName()) + .setTooltip(prog.getProgramPath()), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header()) + .setWidth(250), + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), + (prog) -> topProgramsData.getShortFolderName(prog.getProgramPath(), prog.getProgramName())) + .setWidth(150), + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), + (prog) -> prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes())) + .setWidth(80) + .setCellHorizontalAlignment(HorizontalAlign.RIGHT), + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(), + (prog) -> prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun())) + .setWidth(150) + .setCellHorizontalAlignment(HorizontalAlign.RIGHT) + ))); + + // set up recent domains table + recentDomainsTable = new DataResultJTable<>(new DefaultPojoListTableDataModel<>(Arrays.asList( + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), + (d) -> d.getDomain()) + .setWidth(250), + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(), + (d) -> d.getUrl()) + .setWidth(250), + new DefaultColumnModel( + Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(), + (d) -> DATETIME_FORMAT.format(d.getLastVisit())) + .setWidth(150) + .setCellHorizontalAlignment(HorizontalAlign.RIGHT) + ))); + + // set up acquisition methods + dataFetchComponents = Arrays.asList( + new DataFetchComponents>( + (dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT), + topProgramsTable::setResult), + new DataFetchComponents>( + (dataSource) -> topDomainsData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT), + recentDomainsTable::setResult) + ); + initComponents(); - topProgramsTable.getTableHeader().setReorderingAllowed(false); } /** @@ -81,170 +143,25 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * * @param dataSource The datasource to use in this panel. */ - public void setDataSource(DataSource dataSource) { + public void setDataSource(final DataSource dataSource) { this.dataSource = dataSource; if (dataSource == null || !Case.isCaseOpen()) { - updateTopPrograms(new TopProgramsModel(null)); + dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataLoadingResult.getLoaded(null))); + } else { - updateTopPrograms(getTopProgramsModel(dataSource)); + dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataLoadingResult.getLoading())); + + List> workers = dataFetchComponents + .stream() + .map((components) -> new DataFetchWorker<>(components, dataSource)) + .collect(Collectors.toList()); + + loader.resetLoad(workers); } } - /** - * Updates the Top Programs Table in the gui. - * - * @param data The data in Object[][] form to be used by the - * DefaultTableModel. - */ - private void updateTopPrograms(TopProgramsModel model) { - topProgramsTable.setModel(model); - topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(250); - topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); - topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150); - topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER); - topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80); - topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150); - topProgramsScrollPane.getVerticalScrollBar().setValue(0); - this.repaint(); - } - - /** - * The counts of top programs run. - * - * @param selectedDataSource The DataSource. - * - * @return The JTable data model of counts of program runs. - */ - private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { - List topProgramList - = DataSourceTopProgramsSummary.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - - if (topProgramList == null) { - return new TopProgramsModel(null); - } else { - return new TopProgramsModel(topProgramList); - } - } - - /** - * A POJO defining the values present in the name cell. Defines the name as - * well as the path for the tooltip. - */ - private static class ProgramNameCellValue { - - private final String programName; - private final String programPath; - - ProgramNameCellValue(String programName, String programPath) { - this.programName = programName; - this.programPath = programPath; - } - - @Override - public String toString() { - // override so that the value in the cell reads as programName - return programName; - } - - /** - * @return The program name. - */ - String getProgramName() { - return programName; - } - - /** - * @return The path of the program. - */ - String getProgramPath() { - return programPath; - } - } - - /** - * Defines a cell renderer for the first cell rendering the name as the text - * and path as the tooltip. - */ - private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { - - public Component getTableCellRendererComponent( - JTable table, Object value, - boolean isSelected, boolean hasFocus, - int row, int column) { - JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if (value instanceof ProgramNameCellValue) { - ProgramNameCellValue cellValue = (ProgramNameCellValue) value; - c.setToolTipText(cellValue.getProgramPath()); - } - return c; - } - }; - - /** - * Defines the table model for a JTable of the programs. Accepts a list of - * TopProgramsResult objects as rows data source. - */ - private static class TopProgramsModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - - // column headers for artifact counts table - private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header() - }; - - private final List programResults; - - /** - * Main constructor. - * - * @param programResults The results to display. - */ - TopProgramsModel(List programResults) { - this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); - } - - @Override - public String getColumnName(int column) { - return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; - } - - @Override - public int getRowCount() { - return programResults.size(); - } - - @Override - public int getColumnCount() { - return TOP_PROGS_COLUMN_HEADERS.length; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (rowIndex < 0 || rowIndex >= programResults.size()) { - return null; - } - - TopProgramsResult result = programResults.get(rowIndex); - switch (columnIndex) { - case 0: - return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath()); - case 1: - return DataSourceTopProgramsSummary.getShortFolderName(result.getProgramPath(), result.getProgramName()); - case 2: - return result.getRunTimes(); - case 3: - return result.getLastRun() == null ? null : DATETIME_FORMAT.format(result.getLastRun()); - default: - return null; - } - } - - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -254,40 +171,57 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { + javax.swing.JScrollPane contentScrollPane = new javax.swing.JScrollPane(); + javax.swing.JPanel contentPanel = new javax.swing.JPanel(); javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); - topProgramsScrollPane = new javax.swing.JScrollPane(); - topProgramsTable = new javax.swing.JTable(); + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); + javax.swing.JPanel topProgramsTablePanel = topProgramsTable; + javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); + javax.swing.JLabel recentDomainsLabel = new javax.swing.JLabel(); + javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); + javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable; + setMaximumSize(null); + setLayout(new java.awt.BorderLayout()); + + contentScrollPane.setMaximumSize(null); + contentScrollPane.setMinimumSize(null); + + contentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); + contentPanel.setMaximumSize(new java.awt.Dimension(720, 450)); + contentPanel.setMinimumSize(new java.awt.Dimension(720, 450)); + contentPanel.setLayout(new javax.swing.BoxLayout(contentPanel, javax.swing.BoxLayout.PAGE_AXIS)); + + programsRunLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N + programsRunLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + contentPanel.add(programsRunLabel); + contentPanel.add(filler1); - topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(750, 187)); - topProgramsScrollPane.setViewportView(topProgramsTable); + topProgramsTablePanel.setAlignmentX(0.0F); + topProgramsTablePanel.setMaximumSize(new java.awt.Dimension(700, 187)); + topProgramsTablePanel.setMinimumSize(new java.awt.Dimension(700, 187)); + topProgramsTablePanel.setPreferredSize(new java.awt.Dimension(700, 187)); + contentPanel.add(topProgramsTablePanel); + contentPanel.add(filler3); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(128, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(programsRunLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); + recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel.text")); // NOI18N + contentPanel.add(recentDomainsLabel); + contentPanel.add(filler2); + + recentDomainsTablePanel.setAlignmentX(0.0F); + recentDomainsTablePanel.setMaximumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(700, 187)); + contentPanel.add(recentDomainsTablePanel); + + contentScrollPane.setViewportView(contentPanel); + + add(contentScrollPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane topProgramsScrollPane; - private javax.swing.JTable topProgramsTable; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED new file mode 100644 index 0000000000..1c109cf5c4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED @@ -0,0 +1,2 @@ +DataResultJTable_errorMessage_defaultText=There was an error loading results. +DataResultJTable_loadingMessage_defaultText=Loading results... diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java index 9fa7288daa..a673e8d265 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java @@ -121,24 +121,35 @@ public class DataFetchWorker extends SwingWorker { @Override protected void done() { + // if cancelled, simply return + if (Thread.interrupted() || isCancelled()) { + return; + } + R result = null; try { result = get(); } catch (InterruptedException ignored) { - // if cancelled, set not loaded andt return - resultHandler.accept(DataLoadingResult.getNotLoaded()); + // if cancelled, simply return return; } catch (ExecutionException ex) { - logger.log(Level.WARNING, "There was an error while fetching results.", ex); Throwable inner = ex.getCause(); + // if cancelled during operation, simply return + if (inner != null && inner instanceof InterruptedException) { + return; + } + + // otherwise, there is an error to log + logger.log(Level.WARNING, "There was an error while fetching results.", ex); + if (inner != null && inner instanceof DataProcessorException) { resultHandler.accept(DataLoadingResult.getLoadError((DataProcessorException) inner)); } return; } + // if cancelled, simply return if (Thread.interrupted() || isCancelled()) { - resultHandler.accept(DataLoadingResult.getNotLoaded()); return; } diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java index f341b374a3..d2b1aba199 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java @@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.guiutils.internal; /** * The intermediate or end result of a loading process. */ -class DataLoadingResult { +public class DataLoadingResult { // The state of loading in the result. public enum ProcessorState { diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java index 8671877459..b33fef986d 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java @@ -243,7 +243,7 @@ public class DefaultPojoListTableDataModel extends AbstractTableModel impleme TableColumn col = new TableColumn(i); ColumnModel model = columns.get(i); if (model.getWidth() != null && model.getWidth() >= 0) { - col.setWidth(model.getWidth()); + col.setPreferredWidth(model.getWidth()); } col.setHeaderValue(model.getTitle()); @@ -278,11 +278,15 @@ public class DefaultPojoListTableDataModel extends AbstractTableModel impleme String text = cellModel.getText(); if (StringUtils.isNotBlank(text)) { defaultCell.setText(text); + } else { + defaultCell.setText(null); } String tooltip = cellModel.getTooltip(); if (StringUtils.isNotBlank(tooltip)) { defaultCell.setToolTipText(tooltip); + } else { + defaultCell.setToolTipText(null); } if (columnModel.getCellHorizontalAlignment() != null) { @@ -297,6 +301,8 @@ public class DefaultPojoListTableDataModel extends AbstractTableModel impleme defaultCell.setHorizontalAlignment(JLabel.RIGHT); break; } + } else { + defaultCell.setHorizontalAlignment(JLabel.LEFT); } return defaultCell; From 664deb65bce339df26d66f7cdd17e552bc168b18 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Aug 2020 11:53:34 -0400 Subject: [PATCH 05/16] addressing codacy remarks --- .../DataSourceTopDomainsSummary.java | 4 ++- .../datamodel/SleuthkitCaseProvider.java | 2 +- .../guiutils/internal/DataFetchWorker.java | 16 ++-------- .../guiutils/internal/DataLoadingResult.java | 2 +- .../guiutils/internal/DataProcessor.java | 29 ------------------- .../internal/DataProcessorException.java | 3 +- .../guiutils/internal/DataResultJTable.java | 27 ++++++++--------- .../DefaultPojoListTableDataModel.java | 9 ++++-- 8 files changed, 30 insertions(+), 62 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java index d61a21e307..16cdf98db4 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @@ -41,6 +40,9 @@ public class DataSourceTopDomainsSummary { // this.provider = provider; // } + /* + * a function to calculate a result from 2 paramaters + */ interface Function2 { O apply(A1 a1, A2 a2); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java index 9855fde185..7c369bed63 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java @@ -27,7 +27,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; * uses Case.getCurrentCaseThrows().getSleuthkkitCase(). */ public interface SleuthkitCaseProvider { - public static final SleuthkitCaseProvider DEFAULT = () -> Case.getCurrentCaseThrows().getSleuthkitCase(); + SleuthkitCaseProvider DEFAULT = () -> Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase get() throws NoCurrentCaseException; } diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java index a673e8d265..a3b123ad02 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java @@ -106,17 +106,7 @@ public class DataFetchWorker extends SwingWorker { @Override protected R doInBackground() throws Exception { - if (Thread.interrupted() || isCancelled()) { - throw new InterruptedException(); - } - - R result = processor.process(args); - - if (Thread.interrupted() || isCancelled()) { - throw new InterruptedException(); - } - - return result; + return processor.process(args); } @Override @@ -135,14 +125,14 @@ public class DataFetchWorker extends SwingWorker { } catch (ExecutionException ex) { Throwable inner = ex.getCause(); // if cancelled during operation, simply return - if (inner != null && inner instanceof InterruptedException) { + if (inner instanceof InterruptedException) { return; } // otherwise, there is an error to log logger.log(Level.WARNING, "There was an error while fetching results.", ex); - if (inner != null && inner instanceof DataProcessorException) { + if (inner instanceof DataProcessorException) { resultHandler.accept(DataLoadingResult.getLoadError((DataProcessorException) inner)); } return; diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java index d2b1aba199..2dd85b398b 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java @@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.guiutils.internal; /** * The intermediate or end result of a loading process. */ -public class DataLoadingResult { +public final class DataLoadingResult { // The state of loading in the result. public enum ProcessorState { diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java index f99f33c39a..96aa967dc5 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java @@ -26,35 +26,6 @@ package org.sleuthkit.autopsy.guiutils.internal; */ @FunctionalInterface public interface DataProcessor { - - /** - * Wraps a DataProcessor in statements looking to see if the thread has been - * interrupted and throws an InterruptedException in that event. - * - * @param toBeWrapped The data processor to be wrapped. - * - * @return The wrapped data processor that will throw an interrupted - * exception before or after the toBeWrapped data processor has been - * run. - */ - public static DataProcessor wrap(DataProcessor toBeWrapped) { - return new DataProcessor() { - @Override - public O1 process(I1 input) throws InterruptedException, DataProcessorException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - - O1 output = toBeWrapped.process(input); - if (Thread.interrupted()) { - throw new InterruptedException(); - } - - return output; - } - }; - } - /** * A function that accepts an input argument and outputs a result. Since it * is meant to be used with the DataFetchWorker, it throws an interrupted diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java index cc14647a06..3dc67968c3 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java @@ -23,7 +23,8 @@ package org.sleuthkit.autopsy.guiutils.internal; * DataProcessor. */ public class DataProcessorException extends Exception { - + private static final long serialVersionUID = 1L; + /** * Main constructor. * @param string The error message. diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java index cf747b5259..43d59b4588 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java @@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; "DataResultJTable_errorMessage_defaultText=There was an error loading results." }) public class DataResultJTable extends JPanel { + private static final long serialVersionUID = 1L; /** * JTables don't allow display messages. So this LayerUI is used to display @@ -50,7 +51,8 @@ public class DataResultJTable extends JPanel { * https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html. */ private static class Overlay extends LayerUI { - + private static final long serialVersionUID = 1L; + private final JLabel child; private boolean visible; @@ -96,9 +98,6 @@ public class DataResultJTable extends JPanel { @Override public void paint(Graphics g, JComponent c) { - int w = c.getWidth(); - int h = c.getHeight(); - // Paint the underlying view. super.paint(g, c); @@ -106,6 +105,9 @@ public class DataResultJTable extends JPanel { return; } + int w = c.getWidth(); + int h = c.getHeight(); + // paint the jlabel if visible. child.setBounds(0, 0, w, h); child.paint(g); @@ -119,17 +121,16 @@ public class DataResultJTable extends JPanel { private static final String DEFAULT_NO_RESULTS_MESSAGE = ""; private static final String DEFAULT_NOT_LOADED_MESSAGE = ""; - private final JTable table; private final JScrollPane tableScrollPane; - private final JLayer dualLayer; private final Overlay overlayLayer; + private final PojoListTableDataModel tableModel; private String loadingMessage = DEFAULT_LOADING_MESSAGE; private String errorMessage = DEFAULT_ERROR_MESSAGE; private String noResultsMessage = DEFAULT_NO_RESULTS_MESSAGE; private String notLoadedMessage = DEFAULT_NOT_LOADED_MESSAGE; - private PojoListTableDataModel tableModel = null; + /** * Main constructor. @@ -137,12 +138,12 @@ public class DataResultJTable extends JPanel { */ public DataResultJTable(PojoListTableDataModel tableModel) { this.tableModel = tableModel; - this.table = new JTable(tableModel, tableModel.getTableColumnModel()); - this.table.getTableHeader().setReorderingAllowed(false); + JTable table = new JTable(tableModel, tableModel.getTableColumnModel()); + table.getTableHeader().setReorderingAllowed(false); this.overlayLayer = new Overlay(); this.tableScrollPane = new JScrollPane(table); - this.dualLayer = new JLayer(tableScrollPane, overlayLayer); + JLayer dualLayer = new JLayer(tableScrollPane, overlayLayer); setLayout(new BorderLayout()); add(dualLayer, BorderLayout.CENTER); } @@ -157,21 +158,21 @@ public class DataResultJTable extends JPanel { /** * @return The message shown when there is an exception. */ - String getErrorMessage() { + public String getErrorMessage() { return errorMessage; } /** * @return The message shown when there are no results. */ - String getNoResultsMessage() { + public String getNoResultsMessage() { return noResultsMessage; } /** * @return The message shown when the table has not been loaded. */ - String getNotLoadedMessage() { + public String getNotLoadedMessage() { return notLoadedMessage; } diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java index b33fef986d..9387194eb4 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java @@ -36,7 +36,8 @@ import org.apache.commons.lang3.StringUtils; * class provides a TableModel and TableColumnModel to be used with that class. */ public class DefaultPojoListTableDataModel extends AbstractTableModel implements PojoListTableDataModel { - + private static final long serialVersionUID = 1L; + /** * Describes the horizontal alignment. */ @@ -63,8 +64,7 @@ public class DefaultPojoListTableDataModel extends AbstractTableModel impleme /** * The default cell model. */ - public static class DefaultCellModel implements CellModel { - + public static class DefaultCellModel implements CellModel { private final String text; private String tooltip; @@ -300,6 +300,9 @@ public class DefaultPojoListTableDataModel extends AbstractTableModel impleme case RIGHT: defaultCell.setHorizontalAlignment(JLabel.RIGHT); break; + default: + throw new IllegalArgumentException("Unknown horizontal alignment choice: " + + columnModel.getCellHorizontalAlignment()); } } else { defaultCell.setHorizontalAlignment(JLabel.LEFT); From 2b37bffcaca4bf8877632066cab7513e1a05f9cd Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Aug 2020 12:04:04 -0400 Subject: [PATCH 06/16] addressing codacy remarks --- .../datamodel/DataSourceTopDomainsSummary.java | 4 ++-- .../autopsy/guiutils/internal/DataLoadingResult.java | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java index 16cdf98db4..42fbfb4b97 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -40,8 +40,8 @@ public class DataSourceTopDomainsSummary { // this.provider = provider; // } - /* - * a function to calculate a result from 2 paramaters + /** + * A function to calculate a result from 2 parameters. */ interface Function2 { O apply(A1 a1, A2 a2); diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java index 2dd85b398b..4b6e6b5493 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java @@ -23,7 +23,9 @@ package org.sleuthkit.autopsy.guiutils.internal; */ public final class DataLoadingResult { - // The state of loading in the result. + /** + * The state of loading in the result. + */ public enum ProcessorState { LOADING, NOT_LOADED, LOADED, LOAD_ERROR } From 5e2b3d5335935c7f4dccf4d8bb924c9cdb183062 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Aug 2020 21:01:01 -0400 Subject: [PATCH 07/16] refactoring --- .../DataSourceTopDomainsSummary.java | 40 +- .../datamodel/TopDomainsResult.java | 21 +- .../ui/DataSourceSummaryCountsPanel.java | 24 +- .../ui/DataSourceSummaryDetailsPanel.java | 23 +- .../ui/DataSourceSummaryTabbedPane.java | 27 +- .../DataSourceSummaryUserActivityPanel.form | 5 - .../DataSourceSummaryUserActivityPanel.java | 133 +++---- .../uiutils/Bundle.properties-MERGED | 2 + .../uiutils}/DataFetchWorker.java | 7 +- .../uiutils}/DataLoadingResult.java | 22 +- .../uiutils}/DataProcessor.java | 3 +- .../uiutils}/DataProcessorException.java | 7 +- .../uiutils/DataResultTable.java} | 85 +++-- .../uiutils/DefaultListTableModel.java | 77 ++++ .../uiutils/ListTableModel.java} | 16 +- .../uiutils}/SwingWorkerSequentialRunner.java | 4 +- .../internal/Bundle.properties-MERGED | 2 - .../DefaultPojoListTableDataModel.java | 353 ------------------ 18 files changed, 307 insertions(+), 544 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED rename Core/src/org/sleuthkit/autopsy/{guiutils/internal => datasourcesummary/uiutils}/DataFetchWorker.java (97%) rename Core/src/org/sleuthkit/autopsy/{guiutils/internal => datasourcesummary/uiutils}/DataLoadingResult.java (81%) rename Core/src/org/sleuthkit/autopsy/{guiutils/internal => datasourcesummary/uiutils}/DataProcessor.java (96%) rename Core/src/org/sleuthkit/autopsy/{guiutils/internal => datasourcesummary/uiutils}/DataProcessorException.java (94%) rename Core/src/org/sleuthkit/autopsy/{guiutils/internal/DataResultJTable.java => datasourcesummary/uiutils/DataResultTable.java} (81%) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java rename Core/src/org/sleuthkit/autopsy/{guiutils/internal/PojoListTableDataModel.java => datasourcesummary/uiutils/ListTableModel.java} (70%) rename Core/src/org/sleuthkit/autopsy/{guiutils/internal => datasourcesummary/uiutils}/SwingWorkerSequentialRunner.java (93%) delete mode 100644 Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED delete mode 100644 Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java index 42fbfb4b97..d900c2b172 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -28,36 +28,38 @@ import org.sleuthkit.datamodel.DataSource; * Provides summary information about top domains in a datasource. */ public class DataSourceTopDomainsSummary { + private static final long SLEEP_TIME = 5000; - -// private final SleuthkitCaseProvider provider; -// -// public DataSourceTopDomainsSummary() { -// this(SleuthkitCaseProvider.DEFAULT); -// } -// -// public DataSourceTopDomainsSummary(SleuthkitCaseProvider provider) { -// this.provider = provider; -// } - + /** * A function to calculate a result from 2 parameters. */ - interface Function2 { + interface Function2 { + O apply(A1 a1, A2 a2); } - + + /** + * Gets a list of recent domains based on the datasource. + * + * @param dataSource The datasource to query for recent domains. + * @param count The max count of items to return. + * + * @return The list of items retrieved from the database. + * + * @throws InterruptedException + */ public List getRecentDomains(DataSource dataSource, int count) throws InterruptedException { Thread.sleep(SLEEP_TIME); final String dId = Long.toString(dataSource.getId()); - final Function2 getId = (s,idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx); + final Function2 getId = (s, idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx); return IntStream.range(0, count) .mapToObj(num -> new TopDomainsResult( - getId.apply("domain", num), - getId.apply("url", num), - (long)num, - new Date(120, 1, num) - )) + getId.apply("domain", num), + getId.apply("url", num), + (long) num, + new Date(120, 1, num) + )) .collect(Collectors.toList()); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java index 4d8bc1c012..e843e51c4c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopDomainsResult.java @@ -30,6 +30,14 @@ public class TopDomainsResult { private final Long visitTimes; private final Date lastVisit; + /** + * Describes a top domain result. + * + * @param domain The domain. + * @param url The url. + * @param visitTimes The number of times it was visited. + * @param lastVisit The date of the last visit. + */ public TopDomainsResult(String domain, String url, Long visitTimes, Date lastVisit) { this.domain = domain; this.url = url; @@ -37,21 +45,32 @@ public class TopDomainsResult { this.lastVisit = lastVisit; } + /** + * @return The domain for the result. + */ public String getDomain() { return domain; } + /** + * @return The url for the result. + */ public String getUrl() { return url; } + /** + * @return The number of times this site is visited. + */ public Long getVisitTimes() { return visitTimes; } + /** + * @return The date of the last visit. + */ public Date getLastVisit() { return lastVisit; } - } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java index eff822f161..34a2cb163a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.Map; -import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JLabel; import javax.swing.table.DefaultTableCellRenderer; import org.openide.util.NbBundle.Messages; @@ -39,7 +38,7 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count" }) -class DataSourceSummaryCountsPanel extends javax.swing.JPanel { +class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryTab { private static final long serialVersionUID = 1L; @@ -64,13 +63,10 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() }; - private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); private final FileTypePieChart fileTypePieChart = new FileTypePieChart(); - private DataSource dataSource; - /** * Creates new form DataSourceSummaryCountsPanel */ @@ -82,22 +78,8 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { setDataSource(null); } - /** - * The datasource currently used as the model in this panel. - * - * @return The datasource currently being used as the model in this panel. - */ - public DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the panel. - * - * @param dataSource The datasource to use in this panel. - */ - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; + @Override + protected void onNewDataSource(DataSource dataSource) { if (dataSource == null || !Case.isCaseOpen()) { updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS); } else { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java index f1972d9d89..4a81cb4909 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java @@ -33,7 +33,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Panel to display additional details associated with a specific DataSource */ -class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { +class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryTab { //Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that private static final long serialVersionUID = 1L; @@ -41,8 +41,6 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName()); - private DataSource dataSource; - /** * Creates new form DataSourceSummaryDetailsPanel */ @@ -53,23 +51,8 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { setDataSource(null); } - /** - * The datasource currently used as the model in this panel. - * - * @return The datasource currently being used as the model in this panel. - */ - public DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the panel. - * - * @param dataSource The datasource to use in this panel. - */ - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - + @Override + protected void onNewDataSource(DataSource dataSource) { if (dataSource == null || !Case.isCaseOpen()) { updateDetailsPanelData(null, null, null, null); } else { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 9298646a9d..0dc5ed1309 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -18,10 +18,12 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; +import java.util.Arrays; +import java.util.List; import javax.swing.JTabbedPane; +import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; -import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle; import org.sleuthkit.datamodel.DataSource; /** @@ -39,9 +41,13 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel(); - private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel(); - private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel(); + // A pair of the tab name and the corresponding BaseDataSourceSummaryTabs to be displayed. + private final List> tabs = Arrays.asList( + Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()), + Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), + Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryUserActivityPanel()) + ); + private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); private DataSource dataSource = null; @@ -50,10 +56,10 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { + for (Pair tab : tabs) { + addTab(tab.getKey(), tab.getValue()); + } - addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel); addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel); } @@ -74,9 +80,10 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - detailsPanel.setDataSource(dataSource); - countsPanel.setDataSource(dataSource); - userActivityPanel.setDataSource(dataSource); + for (Pair tab : tabs) { + tab.getValue().setDataSource(dataSource); + } + ingestHistoryPanel.setDataSource(dataSource); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form index 4b19d7b0e3..9798add7eb 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form @@ -1,11 +1,6 @@ - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index 29e74b66b8..533c53bb07 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -31,16 +31,15 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopDomainsSum import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult; -import org.sleuthkit.autopsy.guiutils.internal.DataFetchWorker; -import org.sleuthkit.autopsy.guiutils.internal.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.guiutils.internal.DataLoadingResult; -import org.sleuthkit.autopsy.guiutils.internal.DataResultJTable; -import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel; -import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.DefaultCellModel; -import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.DefaultColumnModel; -import org.sleuthkit.autopsy.guiutils.internal.DefaultPojoListTableDataModel.HorizontalAlign; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.HorizontalAlign; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataLoadingResult; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTable; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtility; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtility.DataResultColumnModel; import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.autopsy.guiutils.internal.SwingWorkerSequentialRunner; /** * A panel to display user activity. @@ -54,69 +53,88 @@ import org.sleuthkit.autopsy.guiutils.internal.SwingWorkerSequentialRunner; "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access",}) -public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { +public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab { private static final long serialVersionUID = 1L; private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); private static final int TOP_PROGS_COUNT = 10; private static final int TOP_DOMAINS_COUNT = 10; - private final SwingWorkerSequentialRunner loader = new SwingWorkerSequentialRunner(); - private final DataResultJTable topProgramsTable; - private final DataResultJTable recentDomainsTable; + private final DataResultTable topProgramsTable; + private final DataResultTable recentDomainsTable; private final List> dataFetchComponents; - private DataSource dataSource; - /** - * Creates new form DataSourceUserActivityPanel + * Creates a new DataSourceUserActivityPanel. */ public DataSourceSummaryUserActivityPanel() { this(new DataSourceTopProgramsSummary(), new DataSourceTopDomainsSummary()); } + /** + * Creates a new DataSourceSummaryUserActivityPanel. + * + * @param topProgramsData Class from which to obtain top programs data. + * @param topDomainsData Class from which to obtain recent domains data. + */ public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) { // set up recent programs table - this.topProgramsTable = new DataResultJTable<>(new DefaultPojoListTableDataModel<>(Arrays.asList( - new DefaultColumnModel( - (prog) -> new DefaultCellModel(prog.getProgramName()) - .setTooltip(prog.getProgramPath()), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header()) - .setWidth(250), - new DefaultColumnModel( + this.topProgramsTable = DataResultTableUtility.getDataResultTable(Arrays.asList( + new DataResultColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + (prog) -> { + return new DefaultCellModel(prog.getProgramName()) + .setTooltip(prog.getProgramPath()); + }, + 250), + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), - (prog) -> topProgramsData.getShortFolderName(prog.getProgramPath(), prog.getProgramName())) - .setWidth(150), - new DefaultColumnModel( + (prog) -> { + return new DefaultCellModel( + topProgramsData.getShortFolderName( + prog.getProgramPath(), + prog.getProgramName())); + }, + 150), + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), - (prog) -> prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes())) - .setWidth(80) - .setCellHorizontalAlignment(HorizontalAlign.RIGHT), - new DefaultColumnModel( + (prog) -> { + String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes()); + return new DefaultCellModel(runTimes) + .setHorizontalAlignment(HorizontalAlign.RIGHT); + }, + 80), + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(), - (prog) -> prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun())) - .setWidth(150) - .setCellHorizontalAlignment(HorizontalAlign.RIGHT) - ))); + (prog) -> { + String date = prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun()); + return new DefaultCellModel(date) + .setHorizontalAlignment(HorizontalAlign.RIGHT); + }, + 150) + )); // set up recent domains table - recentDomainsTable = new DataResultJTable<>(new DefaultPojoListTableDataModel<>(Arrays.asList( - new DefaultColumnModel( + this.recentDomainsTable = DataResultTableUtility.getDataResultTable(Arrays.asList( + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), - (d) -> d.getDomain()) - .setWidth(250), - new DefaultColumnModel( + (d) -> new DefaultCellModel(d.getDomain()), + 250), + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(), - (d) -> d.getUrl()) - .setWidth(250), - new DefaultColumnModel( + (d) -> new DefaultCellModel(d.getUrl()), + 250), + new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(), - (d) -> DATETIME_FORMAT.format(d.getLastVisit())) - .setWidth(150) - .setCellHorizontalAlignment(HorizontalAlign.RIGHT) - ))); + (prog) -> { + String lastVisit = prog.getLastVisit() == null ? "" : DATETIME_FORMAT.format(prog.getLastVisit()); + return new DefaultCellModel(lastVisit) + .setHorizontalAlignment(HorizontalAlign.RIGHT); + }, + 150) + )); - // set up acquisition methods + // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchComponents>( (dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT), @@ -129,22 +147,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { initComponents(); } - /** - * The datasource currently used as the model in this panel. - * - * @return The datasource currently being used as the model in this panel. - */ - public DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the panel. - * - * @param dataSource The datasource to use in this panel. - */ - public void setDataSource(final DataSource dataSource) { - this.dataSource = dataSource; + @Override + protected void onNewDataSource(DataSource dataSource) { if (dataSource == null || !Case.isCaseOpen()) { dataFetchComponents.forEach((item) -> item.getResultHandler() .accept(DataLoadingResult.getLoaded(null))); @@ -158,7 +162,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { .map((components) -> new DataFetchWorker<>(components, dataSource)) .collect(Collectors.toList()); - loader.resetLoad(workers); + getLoader().submit(workers); } } @@ -221,7 +225,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { add(contentScrollPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED new file mode 100644 index 0000000000..934dcb224a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED @@ -0,0 +1,2 @@ +DataResultTable_errorMessage_defaultText=There was an error loading results. +DataResultTable_loadingMessage_defaultText=Loading results... diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java index a3b123ad02..fefc26ea5e 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; @@ -115,7 +115,7 @@ public class DataFetchWorker extends SwingWorker { if (Thread.interrupted() || isCancelled()) { return; } - + R result = null; try { result = get(); @@ -128,7 +128,7 @@ public class DataFetchWorker extends SwingWorker { if (inner instanceof InterruptedException) { return; } - + // otherwise, there is an error to log logger.log(Level.WARNING, "There was an error while fetching results.", ex); @@ -143,6 +143,7 @@ public class DataFetchWorker extends SwingWorker { return; } + // if the data is loaded, send the data to the consumer. resultHandler.accept(DataLoadingResult.getLoaded(result)); } } diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java index 4b6e6b5493..8fe2037a6c 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** * The intermediate or end result of a loading process. @@ -52,26 +52,38 @@ public final class DataLoadingResult { /** * Creates a DataLoadingResult of loaded data including the data. + * * @param data The data. + * * @return The loaded data result. */ public static DataLoadingResult getLoaded(R data) { - return new DataLoadingResult(ProcessorState.LOADED, data, null); + return new DataLoadingResult<>(ProcessorState.LOADED, data, null); } /** * Returns a load error result. + * * @param e The exception (if any) present with the error. - * @return + * + * @return */ - static DataLoadingResult getLoadError(DataProcessorException e) { - return new DataLoadingResult(ProcessorState.LOAD_ERROR, null, e); + public static DataLoadingResult getLoadError(DataProcessorException e) { + return new DataLoadingResult<>(ProcessorState.LOAD_ERROR, null, e); } private final ProcessorState state; private final R data; private final DataProcessorException exception; + /** + * Main constructor for the DataLoadingResult. + * + * @param state The state of the result. + * @param data If the result is LOADED, the data related to this + * result. + * @param exception If the result is LOAD_ERROR, the related exception. + */ private DataLoadingResult(ProcessorState state, R data, DataProcessorException exception) { this.state = state; this.data = data; diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java index 96aa967dc5..f19940ab83 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** * A function that accepts input of type I and outputs type O. This function is @@ -26,6 +26,7 @@ package org.sleuthkit.autopsy.guiutils.internal; */ @FunctionalInterface public interface DataProcessor { + /** * A function that accepts an input argument and outputs a result. Since it * is meant to be used with the DataFetchWorker, it throws an interrupted diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java index 3dc67968c3..ef9477bfcf 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataProcessorException.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java @@ -16,17 +16,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** * An Exception that is thrown when there is an issue processing data in a * DataProcessor. */ public class DataProcessorException extends Exception { + private static final long serialVersionUID = 1L; - + /** * Main constructor. + * * @param string The error message. */ public DataProcessorException(String string) { @@ -35,6 +37,7 @@ public class DataProcessorException extends Exception { /** * Main constructor. + * * @param string The error message. * @param thrwbl The inner exception. */ diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java index 43d59b4588..82f653a8d5 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DataResultJTable.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.BorderLayout; import java.awt.Graphics; @@ -30,6 +30,7 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.plaf.LayerUI; +import javax.swing.table.TableColumnModel; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; @@ -38,11 +39,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; * loading, load error, and not loaded. */ @Messages({ - "DataResultJTable_loadingMessage_defaultText=Loading results...", - "DataResultJTable_errorMessage_defaultText=There was an error loading results." + "DataResultTable_loadingMessage_defaultText=Loading results...", + "DataResultTable_errorMessage_defaultText=There was an error loading results." }) -public class DataResultJTable extends JPanel { - private static final long serialVersionUID = 1L; +public class DataResultTable extends JPanel { /** * JTables don't allow display messages. So this LayerUI is used to display @@ -51,11 +51,15 @@ public class DataResultJTable extends JPanel { * https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html. */ private static class Overlay extends LayerUI { + private static final long serialVersionUID = 1L; - + private final JLabel child; private boolean visible; + /** + * Main constructor for the Overlay. + */ Overlay() { child = new JLabel(); child.setHorizontalAlignment(JLabel.CENTER); @@ -90,6 +94,7 @@ public class DataResultJTable extends JPanel { /** * Sets the message to be displayed in the child jlabel. + * * @param message The message to be displayed. */ void setMessage(String message) { @@ -107,39 +112,41 @@ public class DataResultJTable extends JPanel { int w = c.getWidth(); int h = c.getHeight(); - + // paint the jlabel if visible. child.setBounds(0, 0, w, h); child.paint(g); } } - private static final Logger logger = Logger.getLogger(DataResultJTable.class.getName()); + private static final long serialVersionUID = 1L; - private static final String DEFAULT_LOADING_MESSAGE = Bundle.DataResultJTable_loadingMessage_defaultText(); - private static final String DEFAULT_ERROR_MESSAGE = Bundle.DataResultJTable_errorMessage_defaultText(); + private static final Logger logger = Logger.getLogger(DataResultTable.class.getName()); + + private static final String DEFAULT_LOADING_MESSAGE = Bundle.DataResultTable_loadingMessage_defaultText(); + private static final String DEFAULT_ERROR_MESSAGE = Bundle.DataResultTable_errorMessage_defaultText(); private static final String DEFAULT_NO_RESULTS_MESSAGE = ""; private static final String DEFAULT_NOT_LOADED_MESSAGE = ""; private final JScrollPane tableScrollPane; private final Overlay overlayLayer; - private final PojoListTableDataModel tableModel; + private final ListTableModel tableModel; + private final JTable table; private String loadingMessage = DEFAULT_LOADING_MESSAGE; private String errorMessage = DEFAULT_ERROR_MESSAGE; private String noResultsMessage = DEFAULT_NO_RESULTS_MESSAGE; private String notLoadedMessage = DEFAULT_NOT_LOADED_MESSAGE; - - /** * Main constructor. - * @param tableModel The model to use for the table. + * + * @param tableModel The model to use for the table. */ - public DataResultJTable(PojoListTableDataModel tableModel) { + public DataResultTable(ListTableModel tableModel) { this.tableModel = tableModel; - JTable table = new JTable(tableModel, tableModel.getTableColumnModel()); - table.getTableHeader().setReorderingAllowed(false); + this.table = new JTable(tableModel); + this.table.getTableHeader().setReorderingAllowed(false); this.overlayLayer = new Overlay(); this.tableScrollPane = new JScrollPane(table); @@ -148,6 +155,25 @@ public class DataResultJTable extends JPanel { add(dualLayer, BorderLayout.CENTER); } + /** + * @return The underlying JTable's column model. + */ + public TableColumnModel getColumnModel() { + return this.table.getColumnModel(); + } + + /** + * Sets the underlying JTable's column model. + * + * @param columnModel The table column model to use with the JTable. + * + * @return As a utility, returns this. + */ + public DataResultTable setColumnModel(TableColumnModel columnModel) { + this.table.setColumnModel(columnModel); + return this; + } + /** * @return The message shown when loading. */ @@ -178,40 +204,49 @@ public class DataResultJTable extends JPanel { /** * Sets the loading message. + * * @param loadingMessage The loading message. + * * @return As a utility, returns this. */ - public DataResultJTable setLoadingMessage(String loadingMessage) { + public DataResultTable setLoadingMessage(String loadingMessage) { this.loadingMessage = loadingMessage; return this; } /** * Sets the error message + * * @param errorMessage The error message. + * * @return As a utility, returns this. */ - public DataResultJTable setErrorMessage(String errorMessage) { + public DataResultTable setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; return this; } /** * Sets the message to be shown when no results are present. + * * @param noResultsMessage The 'no results' message. + * * @return As a utility, returns this. */ - public DataResultJTable setNoResultsMessage(String noResultsMessage) { + public DataResultTable setNoResultsMessage(String noResultsMessage) { this.noResultsMessage = noResultsMessage; return this; } /** * Sets the 'not loaded' message. - * @param notLoadedMessage The message to be shown when results are not loaded. + * + * @param notLoadedMessage The message to be shown when results are not + * loaded. + * * @return As a utility, returns this. */ - public DataResultJTable setNotLoadedMessage(String notLoadedMessage) { + public DataResultTable setNotLoadedMessage(String notLoadedMessage) { this.notLoadedMessage = notLoadedMessage; return this; } @@ -234,10 +269,12 @@ public class DataResultJTable extends JPanel { /** * Sets the result to be displayed. + * * @param result The loading result to be displayed. + * * @return As a utility, returns this. */ - public DataResultJTable setResult(DataLoadingResult> result) { + public DataResultTable setResult(DataLoadingResult> result) { if (result == null) { logger.log(Level.SEVERE, "Null data processor result received."); return this; @@ -270,7 +307,7 @@ public class DataResultJTable extends JPanel { logger.log(Level.SEVERE, "No known loading state was found in result."); break; } - + return this; } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java new file mode 100644 index 0000000000..7725c04d3c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java @@ -0,0 +1,77 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.uiutils; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import javax.swing.table.AbstractTableModel; + +/** + * A TableModel for a JTable designed to show a list of data where each item in + * the list represents a row. + */ +public class DefaultListTableModel extends AbstractTableModel implements ListTableModel { + + private static final long serialVersionUID = 1L; + private final List> columns; + private List dataRows = Collections.emptyList(); + + /** + * Main constructor. + * + * @param columns A list of functions where the index of each function + * represents the data to be displayed at each column index. + * The data displayed at row 'r' and column 'c' will be the + * result of columns.get(c).apply(dataRows.get(r)). + */ + public DefaultListTableModel(List> columns) { + this.columns = columns; + } + + @Override + public List getDataRows() { + return dataRows; + } + + @Override + public void setDataRows(List dataRows) { + this.dataRows = dataRows == null ? Collections.emptyList() : Collections.unmodifiableList(dataRows); + super.fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return dataRows.size(); + } + + @Override + public int getColumnCount() { + return columns.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (rowIndex < 0 || rowIndex >= dataRows.size() || columnIndex < 0 || columnIndex >= columns.size()) { + return null; + } + + return columns.get(columnIndex).apply(dataRows.get(rowIndex)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java similarity index 70% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java index 0f05e5e835..c005203d7a 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/PojoListTableDataModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java @@ -16,23 +16,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.util.List; -import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; /** - * An interface to be used with the DataResultJTable that specifies a TableModel - * and TableColumnModel to be used with a JTable based on a list of object type - * T. + * An interface to be used with the DataResultTable that specifies a TableModel + * to be used with a JTable based on a list of object type T. */ -public interface PojoListTableDataModel extends TableModel { - - /** - * @return The TableColumnModel to be used with the jtable. - */ - TableColumnModel getTableColumnModel(); +public interface ListTableModel extends TableModel { /** * @return The list of objects supporting the rows to be displayed in the @@ -42,6 +35,7 @@ public interface PojoListTableDataModel extends TableModel { /** * Sets the list of objects to be displayed in the table. + * * @param dataRows The datarows to be displayed. */ void setDataRows(List dataRows); diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java similarity index 93% rename from Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java index 215b9dc228..1d22220a38 100644 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/SwingWorkerSequentialRunner.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.guiutils.internal; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.util.Collections; import java.util.List; @@ -45,7 +45,7 @@ public class SwingWorkerSequentialRunner { * * @param submittedWorkers The list of submitted swing workers. */ - public synchronized void resetLoad(List> submittedWorkers) { + public synchronized void submit(List> submittedWorkers) { cancelRunning(); if (submittedWorkers == null) { return; diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED deleted file mode 100644 index 1c109cf5c4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/Bundle.properties-MERGED +++ /dev/null @@ -1,2 +0,0 @@ -DataResultJTable_errorMessage_defaultText=There was an error loading results. -DataResultJTable_loadingMessage_defaultText=Loading results... diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java b/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java deleted file mode 100644 index 9387194eb4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/guiutils/internal/DefaultPojoListTableDataModel.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit 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.guiutils.internal; - -import java.awt.Component; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; -import javax.swing.JLabel; -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import org.apache.commons.lang3.StringUtils; - -/** - * The default implementation of a table model for the DataResultJTable. This - * class provides a TableModel and TableColumnModel to be used with that class. - */ -public class DefaultPojoListTableDataModel extends AbstractTableModel implements PojoListTableDataModel { - private static final long serialVersionUID = 1L; - - /** - * Describes the horizontal alignment. - */ - public enum HorizontalAlign { - LEFT, CENTER, RIGHT - } - - /** - * Basic interface for a cell model. - */ - public interface CellModel { - - /** - * @return The text to be shown in the cell. - */ - String getText(); - - /** - * @return The tooltip (if any) to be displayed in the cell. - */ - String getTooltip(); - } - - /** - * The default cell model. - */ - public static class DefaultCellModel implements CellModel { - private final String text; - private String tooltip; - - /** - * Main constructor. - * - * @param text The text to be displayed in the cell. - */ - public DefaultCellModel(String text) { - this.text = text; - } - - @Override - public String getText() { - return text; - } - - @Override - public String getTooltip() { - return tooltip; - } - - /** - * Sets the tooltip for this cell model. - * - * @param tooltip The tooltip for the cell model. - * - * @return As a utility, returns this. - */ - public DefaultCellModel setTooltip(String tooltip) { - this.tooltip = tooltip; - return this; - } - - @Override - public String toString() { - return getText(); - } - } - - /** - * The column model for the table. - */ - public interface ColumnModel { - - /** - * @return The column header title. - */ - String getTitle(); - - /** - * @return A function to generate the contents for the cell based on the - * data in the POJO. - */ - Function getCellCreator(); - - /** - * @return The width of the column to provide to the JTable. This can be - * left as null. - */ - Integer getWidth(); - - /** - * @return The horizontal alignment for the text in the cell. - */ - HorizontalAlign getCellHorizontalAlignment(); - } - - /** - * The default implementation for the column model. - */ - public static class DefaultColumnModel implements ColumnModel { - - private final String title; - private final Function cellCreator; - private Integer width; - private HorizontalAlign cellHorizontalAlignment; - - /** - * Main constructor. - * - * @param title The title for the column header. - * @param retriever Retrieves the value for cell from the POJO provided - * in the row. - */ - public DefaultColumnModel(String title, Function retriever) { - this((obj) -> new DefaultCellModel(retriever.apply(obj)), title); - } - - /** - * Main constructor. - * - * @param retriever Generates a cell model based on the POJO provided - * for the row. - * @param title The title for the column header. - */ - public DefaultColumnModel(Function retriever, String title) { - this.title = title; - this.cellCreator = retriever; - } - - /** - * @return The title for the column header. - */ - public String getTitle() { - return title; - } - - /** - * @return The means of converting the POJO for a row into a cell model - * for this column. - */ - public Function getCellCreator() { - return cellCreator; - } - - /** - * @return The width (if any) to specify for this column in the jtable. - */ - public Integer getWidth() { - return width; - } - - /** - * @return The horizontal alignment of the text in the cells if any. - */ - public HorizontalAlign getCellHorizontalAlignment() { - return cellHorizontalAlignment; - } - - /** - * Sets the width of the column to provide to the jtable. This method - * should be called prior to being provided as an argument to the - * DefaultPojoListTableDataModel. - * - * @param width The width of the column. - * - * @return As a utility, returns this. - */ - public DefaultColumnModel setWidth(Integer width) { - this.width = width; - return this; - } - - /** - * Sets the cell horizontal alignment for the cells in this column. This - * method should be called prior to being provided as an argument to the - * DefaultPojoListTableDataModel. - * - * @param cellHorizontalAlignment The alignment of text in the cell. - * - * @return As a utility, returns this. - */ - public DefaultColumnModel setCellHorizontalAlignment(HorizontalAlign cellHorizontalAlignment) { - this.cellHorizontalAlignment = cellHorizontalAlignment; - return this; - } - } - - private final List> columns; - private List dataRows = Collections.emptyList(); - - /** - * Main constructor - * - * @param columns The model for the columns in this table. - */ - public DefaultPojoListTableDataModel(List> columns) { - this.columns = Collections.unmodifiableList(columns); - } - - @Override - public TableColumnModel getTableColumnModel() { - TableColumnModel toRet = new DefaultTableColumnModel(); - for (int i = 0; i < columns.size(); i++) { - TableColumn col = new TableColumn(i); - ColumnModel model = columns.get(i); - if (model.getWidth() != null && model.getWidth() >= 0) { - col.setPreferredWidth(model.getWidth()); - } - - col.setHeaderValue(model.getTitle()); - - col.setCellRenderer(new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent( - JTable table, Object value, - boolean isSelected, boolean hasFocus, - int row, int column) { - JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if (value instanceof CellModel) { - return DefaultPojoListTableDataModel.this.getTableCellRendererComponent(c, columns.get(column), (CellModel) value); - } else { - return c; - } - } - }); - toRet.addColumn(col); - } - return toRet; - } - - /** - * Customizes the jlabel to match the column model and cell model provided. - * @param defaultCell The cell to customize that will be displayed in the jtable. - * @param columnModel The column model for this cell. - * @param cellModel The cell model for this cell. - * @return The provided defaultCell. - */ - protected Component getTableCellRendererComponent(JLabel defaultCell, ColumnModel columnModel, CellModel cellModel) { - String text = cellModel.getText(); - if (StringUtils.isNotBlank(text)) { - defaultCell.setText(text); - } else { - defaultCell.setText(null); - } - - String tooltip = cellModel.getTooltip(); - if (StringUtils.isNotBlank(tooltip)) { - defaultCell.setToolTipText(tooltip); - } else { - defaultCell.setToolTipText(null); - } - - if (columnModel.getCellHorizontalAlignment() != null) { - switch (columnModel.getCellHorizontalAlignment()) { - case LEFT: - defaultCell.setHorizontalAlignment(JLabel.LEFT); - break; - case CENTER: - defaultCell.setHorizontalAlignment(JLabel.CENTER); - break; - case RIGHT: - defaultCell.setHorizontalAlignment(JLabel.RIGHT); - break; - default: - throw new IllegalArgumentException("Unknown horizontal alignment choice: " + - columnModel.getCellHorizontalAlignment()); - } - } else { - defaultCell.setHorizontalAlignment(JLabel.LEFT); - } - - return defaultCell; - } - - @Override - public List getDataRows() { - return dataRows; - } - - @Override - public void setDataRows(List dataRows) { - this.dataRows = dataRows == null ? Collections.emptyList() : Collections.unmodifiableList(dataRows); - super.fireTableDataChanged(); - } - - @Override - public int getRowCount() { - return dataRows.size(); - } - - @Override - public int getColumnCount() { - return columns.size(); - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (rowIndex < 0 || rowIndex >= dataRows.size() || columnIndex < 0 || columnIndex >= columns.size()) { - return null; - } - - return columns.get(columnIndex).getCellCreator().apply(dataRows.get(rowIndex)); - } - - @Override - public String getColumnName(int column) { - if (column < 0 || column >= columns.size()) { - return null; - } - - return columns.get(column).getTitle(); - } - -} From bf3b2e5a721602c78e9a85a3cd6aacf10ab4ba50 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Aug 2020 21:01:36 -0400 Subject: [PATCH 08/16] refactoring --- .../ui/BaseDataSourceSummaryTab.java | 69 +++++++ .../uiutils/CellModelTableCellRenderer.java | 192 ++++++++++++++++++ .../uiutils/DataResultTableUtility.java | 150 ++++++++++++++ 3 files changed, 411 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java new file mode 100644 index 0000000000..83755c112d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java @@ -0,0 +1,69 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.ui; + +import javax.swing.JPanel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialRunner; +import org.sleuthkit.datamodel.DataSource; + +/** + * + * @author gregd + */ +abstract class BaseDataSourceSummaryTab extends JPanel { + + private final SwingWorkerSequentialRunner loader = new SwingWorkerSequentialRunner(); + private DataSource dataSource; + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ + void setDataSource(DataSource dataSource) { + DataSource oldDataSource = this.dataSource; + this.dataSource = dataSource; + if (this.dataSource != oldDataSource) { + onNewDataSource(this.dataSource); + } + } + + /** + * @return The sequential runner associated with this panel. + */ + protected SwingWorkerSequentialRunner getLoader() { + return loader; + } + + /** + * When a new dataSource is added, this method is called. + * + * @param dataSource The new dataSource. + */ + protected abstract void onNewDataSource(DataSource dataSource); +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java new file mode 100644 index 0000000000..489af38b24 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -0,0 +1,192 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.uiutils; + +import java.awt.Component; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; +import org.apache.commons.lang3.StringUtils; + +/** + * A Table cell renderer that renders a cell of a table based off of the + * CellModel interface provided in the code. + */ +public class CellModelTableCellRenderer extends DefaultTableCellRenderer { + + /** + * Describes the horizontal alignment. + */ + public enum HorizontalAlign { + LEFT(JLabel.LEFT), + CENTER(JLabel.CENTER), + RIGHT(JLabel.RIGHT); + + private final int jlabelAlignment; + + /** + * Constructor for a HorizontalAlign enum. + * + * @param jlabelAlignment The corresponding JLabel horizontal alignment + * number. + */ + HorizontalAlign(int jlabelAlignment) { + this.jlabelAlignment = jlabelAlignment; + } + + /** + * @return The corresponding JLabel horizontal alignment (i.e. + * JLabel.LEFT). + */ + int getJLabelAlignment() { + return this.jlabelAlignment; + } + } + + /** + * Basic interface for a cell model. + */ + public interface CellModel { + + /** + * @return The text to be shown in the cell. + */ + String getText(); + + /** + * @return The tooltip (if any) to be displayed in the cell. + */ + String getTooltip(); + + /** + * @return The horizontal alignment for the text in the cell. + */ + HorizontalAlign getHorizontalAlignment(); + } + + /** + * The default cell model. + */ + public static class DefaultCellModel implements CellModel { + + private final String text; + private String tooltip; + private HorizontalAlign horizontalAlignment; + + /** + * Main constructor. + * + * @param text The text to be displayed in the cell. + */ + public DefaultCellModel(String text) { + this.text = text; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getTooltip() { + return tooltip; + } + + /** + * Sets the tooltip for this cell model. + * + * @param tooltip The tooltip for the cell model. + * + * @return As a utility, returns this. + */ + public DefaultCellModel setTooltip(String tooltip) { + this.tooltip = tooltip; + return this; + } + + @Override + public HorizontalAlign getHorizontalAlignment() { + return horizontalAlignment; + } + + /** + * Sets the horizontal alignment for this cell model. + * + * @param alignment The horizontal alignment for the cell model. + * + * @return As a utility, returns this. + */ + public DefaultCellModel setHorizontalAlignment(HorizontalAlign alignment) { + this.horizontalAlignment = alignment; + return this; + } + + @Override + public String toString() { + return getText(); + } + } + + private static int DEFAULT_ALIGNMENT = JLabel.LEFT; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + + JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (value instanceof CellModel) { + return getTableCellRendererComponent(c, (CellModel) value); + } else { + return c; + } + + } + + /** + * Customizes the jlabel to match the column model and cell model provided. + * + * @param defaultCell The cell to customize that will be displayed in the + * jtable. + * @param cellModel The cell model for this cell. + * + * @return The provided defaultCell. + */ + protected Component getTableCellRendererComponent(JLabel defaultCell, CellModel cellModel) { + String text = cellModel.getText(); + if (StringUtils.isNotBlank(text)) { + defaultCell.setText(text); + } else { + defaultCell.setText(null); + } + + String tooltip = cellModel.getTooltip(); + if (StringUtils.isNotBlank(tooltip)) { + defaultCell.setToolTipText(tooltip); + } else { + defaultCell.setToolTipText(null); + } + + int alignment = (cellModel.getHorizontalAlignment() == null) + ? DEFAULT_ALIGNMENT + : cellModel.getHorizontalAlignment().getJLabelAlignment(); + defaultCell.setHorizontalAlignment(alignment); + + return defaultCell; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java new file mode 100644 index 0000000000..ed1c985562 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java @@ -0,0 +1,150 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datasourcesummary.uiutils; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.swing.table.TableColumnModel; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableColumn; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; + +/** + * Utilitie methods for instantiating aspects of a DataResultTable. + */ +public class DataResultTableUtility { + + /** + * Describes aspects of a column which can be used with getTableModel or + * getDataResultTable. 'T' represents the object that will represent rows in + * the table. + */ + public static class DataResultColumnModel { + + private final String headerTitle; + private final Function cellRenderer; + private final Integer width; + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the + * column based on the data. + */ + public DataResultColumnModel(String headerTitle, Function cellRenderer) { + this(headerTitle, cellRenderer, null); + } + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the + * column based on the data. + * @param width The preferred width of the column. + */ + public DataResultColumnModel(String headerTitle, Function cellRenderer, Integer width) { + this.headerTitle = headerTitle; + this.cellRenderer = cellRenderer; + this.width = width; + } + + /** + * @return The title for the column. + */ + public String getHeaderTitle() { + return headerTitle; + } + + /** + * @return The method that generates a CellModel for the column based on + * the data. + */ + public Function getCellRenderer() { + return cellRenderer; + } + + /** + * @return The preferred width of the column (can be null). + */ + public Integer getWidth() { + return width; + } + } + + private static final CellModelTableCellRenderer renderer = new CellModelTableCellRenderer(); + + /** + * Generates a TableColumnModel based on the column definitions. + * + * @param columns The column definitions. + * + * @return The corresponding TableColumnModel to be used with a JTable. + */ + public static TableColumnModel getTableColumnModel(List> columns) { + TableColumnModel tableModel = new DefaultTableColumnModel(); + + for (int i = 0; i < columns.size(); i++) { + TableColumn col = new TableColumn(i); + DataResultColumnModel model = columns.get(i); + if (model.getWidth() != null && model.getWidth() >= 0) { + col.setPreferredWidth(model.getWidth()); + } + + col.setHeaderValue(model.getHeaderTitle()); + col.setCellRenderer(renderer); + + tableModel.addColumn(col); + } + + return tableModel; + } + + /** + * Generates a ListTableModel based on the column definitions provided where + * 'T' is the object representing each row. + * + * @param columns The column definitions. + * + * @return The corresponding ListTableModel. + */ + public static ListTableModel getTableModel(List> columns) { + List> columnRenderers = columns.stream() + .map((colModel) -> colModel.getCellRenderer()) + .collect(Collectors.toList()); + + return new DefaultListTableModel(columnRenderers); + } + + /** + * Generates a DataResultTable corresponding to the provided column + * definitions where 'T' is the object representing each row. + * + * @param columns The column definitions. + * + * @return The corresponding DataResultTable. + */ + public static DataResultTable getDataResultTable(List> columns) { + ListTableModel tableModel = getTableModel(columns); + DataResultTable resultTable = new DataResultTable<>(tableModel); + return resultTable.setColumnModel(getTableColumnModel(columns)); + } +} From 0b6c00d12bd981356669ccf7cf35f543a4128fd4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Aug 2020 08:19:46 -0400 Subject: [PATCH 09/16] commenting, refactoring, and cleanup --- .../datamodel/DataSourceInfoUtilities.java | 3 +- .../ui/BaseDataSourceSummaryTab.java | 5 ++- .../ui/DataSourceSummaryTabbedPane.java | 4 ++ .../DataSourceSummaryUserActivityPanel.java | 20 ++++++---- .../uiutils/CellModelTableCellRenderer.java | 6 ++- .../uiutils/DataFetchWorker.java | 7 ++-- .../uiutils/DataLoadingResult.java | 8 ++-- .../uiutils/DataProcessor.java | 7 ++-- .../uiutils/DataResultTable.java | 39 ++++++++++++++++--- ...Utility.java => DataResultTableUtils.java} | 9 ++++- .../uiutils/DefaultListTableModel.java | 3 ++ .../uiutils/ListTableModel.java | 2 +- .../uiutils/SwingWorkerSequentialRunner.java | 5 +++ 13 files changed, 86 insertions(+), 32 deletions(-) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{DataResultTableUtility.java => DataResultTableUtils.java} (94%) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index 1f5f6e362f..39c0129728 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -110,8 +110,7 @@ final class DataSourceInfoUtilities { static T getBaseQueryResult(String query, ResultSetHandler processor, String errorMessage) { return getBaseQueryResult(SleuthkitCaseProvider.DEFAULT, query, processor, errorMessage); } - - + /** * Retrieves a result based on the provided query. * diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java index 83755c112d..08e6e8becb 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java @@ -23,11 +23,12 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialRunn import org.sleuthkit.datamodel.DataSource; /** - * - * @author gregd + * Base class from which other tabs in data source summary derive. */ abstract class BaseDataSourceSummaryTab extends JPanel { + private static final long serialVersionUID = 1L; + private final SwingWorkerSequentialRunner loader = new SwingWorkerSequentialRunner(); private DataSource dataSource; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 0dc5ed1309..5b732f0992 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -60,6 +60,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { addTab(tab.getKey(), tab.getValue()); } + // IngestJobInfoPanel is not specifically a data source summary panel + // and is called separately for that reason. addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel); } @@ -84,6 +86,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { tab.getValue().setDataSource(dataSource); } + // IngestJobInfoPanel is not specifically a data source summary panel + // and is called separately for that reason. ingestHistoryPanel.setDataSource(dataSource); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index 0939adbd47..c33d6997cd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -37,8 +37,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataLoadingResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtility; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtility.DataResultColumnModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils.DataResultColumnModel; import org.sleuthkit.datamodel.DataSource; /** @@ -53,8 +53,7 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access", - "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists", -}) + "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists",}) public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab { private static final long serialVersionUID = 1L; @@ -81,7 +80,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab */ public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) { // set up recent programs table - this.topProgramsTable = DataResultTableUtility.getDataResultTable(Arrays.asList( + this.topProgramsTable = DataResultTableUtils.getDataResultTable(Arrays.asList( new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), (prog) -> { @@ -118,7 +117,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab this.topProgramsTable.setNoResultsMessage(Bundle.DataSourceSummaryUserActivityPanel_noDataExists()); // set up recent domains table - this.recentDomainsTable = DataResultTableUtility.getDataResultTable(Arrays.asList( + this.recentDomainsTable = DataResultTableUtils.getDataResultTable(Arrays.asList( new DataResultColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), (d) -> new DefaultCellModel(d.getDomain()), @@ -153,19 +152,24 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab @Override protected void onNewDataSource(DataSource dataSource) { + // if no data source is present or the case is not open, + // set results for tables to null. if (dataSource == null || !Case.isCaseOpen()) { dataFetchComponents.forEach((item) -> item.getResultHandler() - .accept(DataLoadingResult.getLoaded(null))); + .accept(DataLoadingResult.getLoadedResult(null))); } else { + // set tables to display loading screen dataFetchComponents.forEach((item) -> item.getResultHandler() - .accept(DataLoadingResult.getLoading())); + .accept(DataLoadingResult.getLoadingResult())); + // create swing workers to run for each table List> workers = dataFetchComponents .stream() .map((components) -> new DataFetchWorker<>(components, dataSource)) .collect(Collectors.toList()); + // submit swing workers to run getLoader().submit(workers); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 489af38b24..f5b72fccf6 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -26,7 +26,7 @@ import org.apache.commons.lang3.StringUtils; /** * A Table cell renderer that renders a cell of a table based off of the - * CellModel interface provided in the code. + * CellModel interface provided within this class. */ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { @@ -168,6 +168,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * @return The provided defaultCell. */ protected Component getTableCellRendererComponent(JLabel defaultCell, CellModel cellModel) { + // sets the text for the cell or null if not present. String text = cellModel.getText(); if (StringUtils.isNotBlank(text)) { defaultCell.setText(text); @@ -175,6 +176,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { defaultCell.setText(null); } + // sets the tooltip for the cell if present. String tooltip = cellModel.getTooltip(); if (StringUtils.isNotBlank(tooltip)) { defaultCell.setToolTipText(tooltip); @@ -182,6 +184,8 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { defaultCell.setToolTipText(null); } + // sets the JLabel alignment (left, center, right) or default alignment + // if no alignment is specified int alignment = (cellModel.getHorizontalAlignment() == null) ? DEFAULT_ALIGNMENT : cellModel.getHorizontalAlignment().getJLabelAlignment(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java index fefc26ea5e..fda9f91cc1 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java @@ -25,7 +25,8 @@ import javax.swing.SwingWorker; import org.sleuthkit.autopsy.coreutils.Logger; /** - * A Swing worker data-fetching with result-handler class. + * A Swing worker that accepts an argument of a data processor and a result + * handler. */ public class DataFetchWorker extends SwingWorker { @@ -133,7 +134,7 @@ public class DataFetchWorker extends SwingWorker { logger.log(Level.WARNING, "There was an error while fetching results.", ex); if (inner instanceof DataProcessorException) { - resultHandler.accept(DataLoadingResult.getLoadError((DataProcessorException) inner)); + resultHandler.accept(DataLoadingResult.getLoadErrorResult((DataProcessorException) inner)); } return; } @@ -144,6 +145,6 @@ public class DataFetchWorker extends SwingWorker { } // if the data is loaded, send the data to the consumer. - resultHandler.accept(DataLoadingResult.getLoaded(result)); + resultHandler.accept(DataLoadingResult.getLoadedResult(result)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java index 8fe2037a6c..3b12474db0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java @@ -39,14 +39,14 @@ public final class DataLoadingResult { /** * @return Returns a data loading result. */ - public static DataLoadingResult getLoading() { + public static DataLoadingResult getLoadingResult() { return (DataLoadingResult) LOADING; } /** * @return Returns a 'not loaded' result. */ - public static DataLoadingResult getNotLoaded() { + public static DataLoadingResult getNotLoadedResult() { return (DataLoadingResult) NOT_LOADED; } @@ -57,7 +57,7 @@ public final class DataLoadingResult { * * @return The loaded data result. */ - public static DataLoadingResult getLoaded(R data) { + public static DataLoadingResult getLoadedResult(R data) { return new DataLoadingResult<>(ProcessorState.LOADED, data, null); } @@ -68,7 +68,7 @@ public final class DataLoadingResult { * * @return */ - public static DataLoadingResult getLoadError(DataProcessorException e) { + public static DataLoadingResult getLoadErrorResult(DataProcessorException e) { return new DataLoadingResult<>(ProcessorState.LOAD_ERROR, null, e); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java index f19940ab83..8f5b77f6d9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java @@ -30,15 +30,16 @@ public interface DataProcessor { /** * A function that accepts an input argument and outputs a result. Since it * is meant to be used with the DataFetchWorker, it throws an interrupted - * exception if the thread has been interrupted and data processing + * exception if the thread has been interrupted. It throws a data processing * exception if there is an error during processing. * * @param input The input argument. * * @return The output result. * - * @throws InterruptedException - * @throws DataProcessorException + * @throws InterruptedException Thrown if the operation is cancelled. + * @throws DataProcessorException Thrown if there is an issue processing the + * request. */ O process(I input) throws InterruptedException, DataProcessorException; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java index 82f653a8d5..db179b0408 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java @@ -35,7 +35,7 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; /** - * A JTable that displays a list of POJO items and also can display messages for + * A table that displays a list of items and also can display messages for * loading, load error, and not loaded. */ @Messages({ @@ -45,9 +45,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; public class DataResultTable extends JPanel { /** - * JTables don't allow display messages. So this LayerUI is used to display - * the contents of a child JLabel. Inspired by TableWaitLayerTest (Animating - * a Busy Indicator): + * JTables don't allow displaying messages. So this LayerUI is used to + * display the contents of a child JLabel. Inspired by TableWaitLayerTest + * (Animating a Busy Indicator): * https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html. */ private static class Overlay extends LayerUI { @@ -251,20 +251,34 @@ public class DataResultTable extends JPanel { return this; } + /** + * Sets the data to be shown in the JTable. + * + * @param data The list of data objects to be shown. + */ private void setResultList(List data) { + // set the list of data to be shown as either the data or an empty list + // on null. List dataToSet = (data != null) ? Collections.unmodifiableList(data) : Collections.emptyList(); + // since the data is being reset, scroll to the top. tableScrollPane.getVerticalScrollBar().setValue(0); + + // set the underlying table model's data. this.tableModel.setDataRows(dataToSet); - repaint(); } + /** + * Sets the message and visibility of the overlay. + * + * @param visible The visibility of the overlay. + * @param message The message in the overlay. + */ private void setOverlay(boolean visible, String message) { this.overlayLayer.setVisible(visible); this.overlayLayer.setMessage(message); - repaint(); } /** @@ -283,31 +297,44 @@ public class DataResultTable extends JPanel { switch (result.getState()) { case LOADED: if (result.getData() == null || result.getData().size() <= 0) { + // if loaded and there is no data, set an empty result list + // and set the overlay to show no results message. setResultList(null); setOverlay(true, getNoResultsMessage()); } else { + // otherwise show the data and remove the overlay. setResultList(result.getData()); setOverlay(false, null); } break; case NOT_LOADED: + // if results are not loaded, empty results list and display + // not loaded message setResultList(null); setOverlay(true, getNotLoadedMessage()); break; case LOADING: + // if this table is loading, then set the results to empty and + // display the loading message. setResultList(null); setOverlay(true, getLoadingMessage()); break; case LOAD_ERROR: + // if there is an error, log accordingly, set result list to + // empty and display error message logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException()); setResultList(null); setOverlay(true, getErrorMessage()); break; default: + // an unknown loading state was specified. log accordingly. logger.log(Level.SEVERE, "No known loading state was found in result."); break; } + // repaint to capture changes. + repaint(); + return this; } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java index ed1c985562..5198d54b03 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtility.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java @@ -27,9 +27,9 @@ import javax.swing.table.TableColumn; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; /** - * Utilitie methods for instantiating aspects of a DataResultTable. + * Utility methods for instantiating aspects of a DataResultTable. */ -public class DataResultTableUtility { +public class DataResultTableUtils { /** * Describes aspects of a column which can be used with getTableModel or @@ -105,11 +105,16 @@ public class DataResultTableUtility { for (int i = 0; i < columns.size(); i++) { TableColumn col = new TableColumn(i); DataResultColumnModel model = columns.get(i); + // if a preferred width is specified in the column definition, + // set the underlying TableColumn preferred width. if (model.getWidth() != null && model.getWidth() >= 0) { col.setPreferredWidth(model.getWidth()); } + // set the title col.setHeaderValue(model.getHeaderTitle()); + + // use the cell model renderer in this instance col.setCellRenderer(renderer); tableModel.addColumn(col); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java index 7725c04d3c..d1e940837a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java @@ -68,10 +68,13 @@ public class DefaultListTableModel extends AbstractTableModel implements List @Override public Object getValueAt(int rowIndex, int columnIndex) { + // if index requested is null, return null if (rowIndex < 0 || rowIndex >= dataRows.size() || columnIndex < 0 || columnIndex >= columns.size()) { return null; } + // otherwise, get the corresponding row and use the corresponding + // column function to get the value return columns.get(columnIndex).apply(dataRows.get(rowIndex)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java index c005203d7a..e25b53a3ad 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java @@ -23,7 +23,7 @@ import javax.swing.table.TableModel; /** * An interface to be used with the DataResultTable that specifies a TableModel - * to be used with a JTable based on a list of object type T. + * to be used with the underlying JTable based on a list of object type T. */ public interface ListTableModel extends TableModel { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java index 1d22220a38..e0d0e711b0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java @@ -46,12 +46,17 @@ public class SwingWorkerSequentialRunner { * @param submittedWorkers The list of submitted swing workers. */ public synchronized void submit(List> submittedWorkers) { + // cancel currently running operations cancelRunning(); + + // if no workers, there is nothing to run if (submittedWorkers == null) { return; } this.workers = Collections.unmodifiableList(submittedWorkers); + + // start running the workers and capture the futures if there is a need to cancel them. this.futures = this.workers.stream() .map((w) -> executorService.submit(w)) .collect(Collectors.toList()); From 67eaa71de9e53b8a73b22fd75d2f25a8f7aed7ec Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Aug 2020 08:28:28 -0400 Subject: [PATCH 10/16] codacy remarks --- .../autopsy/datasourcesummary/ui/Bundle.properties-MERGED | 1 + .../datasourcesummary/uiutils/CellModelTableCellRenderer.java | 2 ++ .../datasourcesummary/uiutils/DataResultTableUtils.java | 3 +++ 3 files changed, 6 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index ec8b2e1e21..98f650b3de 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -75,6 +75,7 @@ DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains +DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index f5b72fccf6..cb826fefbd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -30,6 +30,8 @@ import org.apache.commons.lang3.StringUtils; */ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 1L; + /** * Describes the horizontal alignment. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java index 5198d54b03..df5ecb40e9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java @@ -152,4 +152,7 @@ public class DataResultTableUtils { DataResultTable resultTable = new DataResultTable<>(tableModel); return resultTable.setColumnModel(getTableColumnModel(columns)); } + + private DataResultTableUtils() { + } } From acc8110061c701614a06c8d27512d68f9657f4a0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 09:01:04 -0400 Subject: [PATCH 11/16] applying feedback --- .../DataSourceTopDomainsSummary.java | 3 +- .../datamodel/SleuthkitCaseProvider.java | 46 +++++++++++++++++-- ...b.java => BaseDataSourceSummaryPanel.java} | 21 +++++---- .../ui/DataSourceBrowser.java | 1 + .../ui/DataSourceSummaryCountsPanel.java | 3 +- .../ui/DataSourceSummaryDetailsPanel.java | 2 +- .../ui/DataSourceSummaryTabbedPane.java | 6 +-- .../DataSourceSummaryUserActivityPanel.java | 10 ++-- .../uiutils/DataResultTableUtils.java | 4 +- ...{DataResultTable.java => JTablePanel.java} | 18 ++++---- .../NonEditableTableModel.java | 15 ++++-- .../RightAlignedTableCellRenderer.java | 4 +- ...ava => SwingWorkerSequentialExecutor.java} | 5 +- 13 files changed, 95 insertions(+), 43 deletions(-) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/{BaseDataSourceSummaryTab.java => BaseDataSourceSummaryPanel.java} (73%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{DataResultTable.java => JTablePanel.java} (94%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/{ui => uiutils}/NonEditableTableModel.java (74%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/{ui => uiutils}/RightAlignedTableCellRenderer.java (94%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{SwingWorkerSequentialRunner.java => SwingWorkerSequentialExecutor.java} (94%) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java index d900c2b172..547d8db88c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -25,7 +25,8 @@ import java.util.stream.IntStream; import org.sleuthkit.datamodel.DataSource; /** - * Provides summary information about top domains in a datasource. + * Provides summary information about top domains in a datasource. At this time, + * the data being provided is fictitious and is done as a placeholder. */ public class DataSourceTopDomainsSummary { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java index f734b1137c..3f1bd24177 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java @@ -23,21 +23,57 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.SleuthkitCase; /** - * An interface to provide the current SleuthkitCase object. By default, this - * uses Case.getCurrentCaseThrows().getSleuthkkitCase(). + * An interface to provide the current SleuthkitCase object. This is to allow + * for SleuthkitCase objects to be created and injected in a testing scenario + * outside of the context of Case. + * + * By default, this uses Case.getCurrentCaseThrows().getSleuthkitCase(). */ public interface SleuthkitCaseProvider { + /** + * Exception thrown in the event that the SleuthkitCase object cannot be + * provided. + */ + public static class SleuthkitCaseProviderException extends Exception { + + /** + * Main constructor. + * + * @param string The message for the exception. + */ + public SleuthkitCaseProviderException(String string) { + super(string); + } + + /** + * Main constructor. + * + * @param string The message for the exception. + * @param thrwbl The inner exception. + */ + public SleuthkitCaseProviderException(String string, Throwable thrwbl) { + super(string, thrwbl); + } + } + /** * The default SleuthkitCaseProvider. This uses * Case.getCurrentCaseThrows().getSleuthkitCase(). */ - SleuthkitCaseProvider DEFAULT = () -> Case.getCurrentCaseThrows().getSleuthkitCase(); + SleuthkitCaseProvider DEFAULT = () -> { + try { + return Case.getCurrentCaseThrows().getSleuthkitCase(); + } catch (NoCurrentCaseException e) { + throw new SleuthkitCaseProviderException("No currently open case.", e); + } + }; /** * @return Returns the current SleuthkitCase object. * - * @throws NoCurrentCaseException Thrown if no case is open. + * @throws SleuthkitCaseProviderException Thrown if there is an error + * providing the case. */ - SleuthkitCase get() throws NoCurrentCaseException; + SleuthkitCase get() throws SleuthkitCaseProviderException; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java similarity index 73% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 08e6e8becb..dc31c9a77f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryTab.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -18,18 +18,20 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; +import java.util.List; import javax.swing.JPanel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialRunner; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor; import org.sleuthkit.datamodel.DataSource; /** * Base class from which other tabs in data source summary derive. */ -abstract class BaseDataSourceSummaryTab extends JPanel { +abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; - private final SwingWorkerSequentialRunner loader = new SwingWorkerSequentialRunner(); + private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor(); private DataSource dataSource; /** @@ -37,7 +39,7 @@ abstract class BaseDataSourceSummaryTab extends JPanel { * * @return The datasource currently being used as the model in this panel. */ - DataSource getDataSource() { + synchronized DataSource getDataSource() { return dataSource; } @@ -46,7 +48,7 @@ abstract class BaseDataSourceSummaryTab extends JPanel { * * @param dataSource The datasource to use in this panel. */ - void setDataSource(DataSource dataSource) { + synchronized void setDataSource(DataSource dataSource) { DataSource oldDataSource = this.dataSource; this.dataSource = dataSource; if (this.dataSource != oldDataSource) { @@ -55,10 +57,13 @@ abstract class BaseDataSourceSummaryTab extends JPanel { } /** - * @return The sequential runner associated with this panel. + * Submits the following swing workers for execution in sequential order. If + * there are any previous workers, those workers are cancelled. + * + * @param workers The workers to submit for execution. */ - protected SwingWorkerSequentialRunner getLoader() { - return loader; + protected void submit(List> workers) { + executor.submit(workers); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java index a31bbb03f7..98522e5071 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer; import java.awt.EventQueue; import java.beans.PropertyVetoException; import javax.swing.ListSelectionModel; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java index 91479833c9..306a036941 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.NonEditableTableModel; import java.util.Map; import javax.swing.JLabel; import javax.swing.table.DefaultTableCellRenderer; @@ -37,7 +38,7 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count" }) -class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryTab { +class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java index 4a81cb4909..3bc0eaf36d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java @@ -33,7 +33,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Panel to display additional details associated with a specific DataSource */ -class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryTab { +class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel { //Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 5b732f0992..7e0cd951c3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -42,7 +42,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; // A pair of the tab name and the corresponding BaseDataSourceSummaryTabs to be displayed. - private final List> tabs = Arrays.asList( + private final List> tabs = Arrays.asList( Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryUserActivityPanel()) @@ -56,7 +56,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { - for (Pair tab : tabs) { + for (Pair tab : tabs) { addTab(tab.getKey(), tab.getValue()); } @@ -82,7 +82,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - for (Pair tab : tabs) { + for (Pair tab : tabs) { tab.getValue().setDataSource(dataSource); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index c33d6997cd..04820c42d3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRendere import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataLoadingResult; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTable; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils.DataResultColumnModel; import org.sleuthkit.datamodel.DataSource; @@ -54,15 +54,15 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access", "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists",}) -public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab { +public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); private static final int TOP_PROGS_COUNT = 10; private static final int TOP_DOMAINS_COUNT = 10; - private final DataResultTable topProgramsTable; - private final DataResultTable recentDomainsTable; + private final JTablePanel topProgramsTable; + private final JTablePanel recentDomainsTable; private final List> dataFetchComponents; /** @@ -170,7 +170,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryTab .collect(Collectors.toList()); // submit swing workers to run - getLoader().submit(workers); + submit(workers); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java index df5ecb40e9..514e2363a8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java @@ -147,9 +147,9 @@ public class DataResultTableUtils { * * @return The corresponding DataResultTable. */ - public static DataResultTable getDataResultTable(List> columns) { + public static JTablePanel getDataResultTable(List> columns) { ListTableModel tableModel = getTableModel(columns); - DataResultTable resultTable = new DataResultTable<>(tableModel); + JTablePanel resultTable = new JTablePanel<>(tableModel); return resultTable.setColumnModel(getTableColumnModel(columns)); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index db179b0408..ad3c7cd930 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTable.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -42,7 +42,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; "DataResultTable_loadingMessage_defaultText=Loading results...", "DataResultTable_errorMessage_defaultText=There was an error loading results." }) -public class DataResultTable extends JPanel { +public class JTablePanel extends JPanel { /** * JTables don't allow displaying messages. So this LayerUI is used to @@ -121,7 +121,7 @@ public class DataResultTable extends JPanel { private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(DataResultTable.class.getName()); + private static final Logger logger = Logger.getLogger(JTablePanel.class.getName()); private static final String DEFAULT_LOADING_MESSAGE = Bundle.DataResultTable_loadingMessage_defaultText(); private static final String DEFAULT_ERROR_MESSAGE = Bundle.DataResultTable_errorMessage_defaultText(); @@ -143,7 +143,7 @@ public class DataResultTable extends JPanel { * * @param tableModel The model to use for the table. */ - public DataResultTable(ListTableModel tableModel) { + public JTablePanel(ListTableModel tableModel) { this.tableModel = tableModel; this.table = new JTable(tableModel); this.table.getTableHeader().setReorderingAllowed(false); @@ -169,7 +169,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setColumnModel(TableColumnModel columnModel) { + public JTablePanel setColumnModel(TableColumnModel columnModel) { this.table.setColumnModel(columnModel); return this; } @@ -209,7 +209,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setLoadingMessage(String loadingMessage) { + public JTablePanel setLoadingMessage(String loadingMessage) { this.loadingMessage = loadingMessage; return this; } @@ -221,7 +221,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setErrorMessage(String errorMessage) { + public JTablePanel setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; return this; } @@ -233,7 +233,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setNoResultsMessage(String noResultsMessage) { + public JTablePanel setNoResultsMessage(String noResultsMessage) { this.noResultsMessage = noResultsMessage; return this; } @@ -246,7 +246,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setNotLoadedMessage(String notLoadedMessage) { + public JTablePanel setNotLoadedMessage(String notLoadedMessage) { this.notLoadedMessage = notLoadedMessage; return this; } @@ -288,7 +288,7 @@ public class DataResultTable extends JPanel { * * @return As a utility, returns this. */ - public DataResultTable setResult(DataLoadingResult> result) { + public JTablePanel setResult(DataLoadingResult> result) { if (result == null) { logger.log(Level.SEVERE, "Null data processor result received."); return this; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/NonEditableTableModel.java similarity index 74% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/NonEditableTableModel.java index 3274415a89..0461e514d8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/NonEditableTableModel.java @@ -16,17 +16,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import javax.swing.table.DefaultTableModel; /** * A Table model where cells are not editable. */ -class NonEditableTableModel extends DefaultTableModel { +public class NonEditableTableModel extends DefaultTableModel { + private static final long serialVersionUID = 1L; - - NonEditableTableModel(Object[][] data, Object[] columnNames) { + + /** + * Main constructor. + * + * @param data The data to be displayed. + * @param columnNames The column names. + */ + public NonEditableTableModel(Object[][] data, Object[] columnNames) { super(data, columnNames); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/RightAlignedTableCellRenderer.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/RightAlignedTableCellRenderer.java index 989e97da82..6869ceea11 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/RightAlignedTableCellRenderer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.Component; import java.lang.reflect.InvocationTargetException; @@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer; * cell contains a NodeProperty the value of that NodeProperty sets text to * empty string if null. */ -final class RightAlignedTableCellRenderer extends GrayableCellRenderer { +public class RightAlignedTableCellRenderer extends GrayableCellRenderer { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialExecutor.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialExecutor.java index e0d0e711b0..54abdb0ad9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialRunner.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/SwingWorkerSequentialExecutor.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; @@ -33,7 +34,7 @@ import javax.swing.SwingWorker; * Based on: * https://stackoverflow.com/questions/31580805/java-swingworker-one-after-another-and-update-gui */ -public class SwingWorkerSequentialRunner { +public class SwingWorkerSequentialExecutor { private final ExecutorService executorService = Executors.newFixedThreadPool(1); private List> workers = Collections.emptyList(); @@ -54,7 +55,7 @@ public class SwingWorkerSequentialRunner { return; } - this.workers = Collections.unmodifiableList(submittedWorkers); + this.workers = new ArrayList<>(submittedWorkers); // start running the workers and capture the futures if there is a need to cancel them. this.futures = this.workers.stream() From 5147cf595bee7a1a5957387b0a1e69ab12380a51 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 09:17:21 -0400 Subject: [PATCH 12/16] applying feedback --- .../datamodel/DataSourceInfoUtilities.java | 3 ++- .../datamodel/DataSourceTopDomainsSummary.java | 2 +- .../datamodel/DataSourceTopProgramsSummary.java | 2 +- .../datasourcesummary/uiutils/DataLoadingResult.java | 8 ++++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index 39c0129728..f90584a87f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -27,6 +27,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM; @@ -131,7 +132,7 @@ final class DataSourceInfoUtilities { } catch (SQLException ex) { logger.log(Level.WARNING, errorMessage, ex); } - } catch (TskCoreException | NoCurrentCaseException ex) { + } catch (TskCoreException | SleuthkitCaseProviderException ex) { logger.log(Level.WARNING, errorMessage, ex); } return null; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java index 547d8db88c..cc97b62389 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java @@ -59,7 +59,7 @@ public class DataSourceTopDomainsSummary { getId.apply("domain", num), getId.apply("url", num), (long) num, - new Date(120, 1, num) + new Date(((long) num) * 1000 * 60 * 60 * 24) )) .collect(Collectors.toList()); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java index 8bc527f2bb..495f706f0b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java @@ -277,7 +277,7 @@ public class DataSourceTopProgramsSummary { * * @return The short folder name or empty string if not found. */ - public static String getShortFolderName(String strPath, String applicationName) { + public String getShortFolderName(String strPath, String applicationName) { if (strPath == null) { return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java index 3b12474db0..d2cd41dbe8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; +import java.util.Collections; + /** * The intermediate or end result of a loading process. */ @@ -31,14 +33,15 @@ public final class DataLoadingResult { } // Since loading doesn't have any typed arguments, a static final instance is used. - private static final DataLoadingResult LOADING = new DataLoadingResult(ProcessorState.LOADING, null, null); + private static final DataLoadingResult LOADING = new DataLoadingResult<>(ProcessorState.LOADING, null, null); // Since not loaded doesn't have any typed arguments, a static final instance is used. - private static final DataLoadingResult NOT_LOADED = new DataLoadingResult(ProcessorState.LOADED, null, null); + private static final DataLoadingResult NOT_LOADED = new DataLoadingResult<>(ProcessorState.LOADED, null, null); /** * @return Returns a data loading result. */ + @SuppressWarnings("unchecked") public static DataLoadingResult getLoadingResult() { return (DataLoadingResult) LOADING; } @@ -46,6 +49,7 @@ public final class DataLoadingResult { /** * @return Returns a 'not loaded' result. */ + @SuppressWarnings("unchecked") public static DataLoadingResult getNotLoadedResult() { return (DataLoadingResult) NOT_LOADED; } From e5da81f349728c87ba86ac93a6ca49b11267b102 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 10:33:31 -0400 Subject: [PATCH 13/16] addressing codacy remarks --- .../datasourcesummary/datamodel/SleuthkitCaseProvider.java | 4 +++- .../autopsy/datasourcesummary/uiutils/DataLoadingResult.java | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java index 3f1bd24177..9c5928d6b2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java @@ -35,7 +35,9 @@ public interface SleuthkitCaseProvider { * Exception thrown in the event that the SleuthkitCase object cannot be * provided. */ - public static class SleuthkitCaseProviderException extends Exception { + static class SleuthkitCaseProviderException extends Exception { + + private static final long serialVersionUID = 1L; /** * Main constructor. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java index d2cd41dbe8..225dbf7382 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; -import java.util.Collections; - /** * The intermediate or end result of a loading process. */ From 622544675547ded7151dc38374afeffa2821e227 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 14:23:59 -0400 Subject: [PATCH 14/16] revisions and refactoring --- .../DataSourceSummaryUserActivityPanel.java | 60 +-- .../uiutils/Bundle.properties-MERGED | 5 +- ...oadingResult.java => DataFetchResult.java} | 40 +- .../uiutils/DataFetchWorker.java | 26 +- .../{DataProcessor.java => DataFetcher.java} | 10 +- ...ception.java => DataFetcherException.java} | 6 +- .../uiutils/DataResultTableUtils.java | 158 -------- .../uiutils/DefaultListTableModel.java | 3 +- .../uiutils/JTablePanel.java | 358 +++++++++++------- .../uiutils/ListTableModel.java | 4 +- 10 files changed, 290 insertions(+), 380 deletions(-) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{DataLoadingResult.java => DataFetchResult.java} (58%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{DataProcessor.java => DataFetcher.java} (82%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/{DataProcessorException.java => DataFetcherException.java} (87%) delete mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index 04820c42d3..393c429c46 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -35,10 +35,9 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRendere import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.HorizontalAlign; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataLoadingResult; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataResultTableUtils.DataResultColumnModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; import org.sleuthkit.datamodel.DataSource; /** @@ -64,6 +63,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan private final JTablePanel topProgramsTable; private final JTablePanel recentDomainsTable; private final List> dataFetchComponents; + private final List> tables; /** * Creates a new DataSourceUserActivityPanel. @@ -80,15 +80,14 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan */ public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) { // set up recent programs table - this.topProgramsTable = DataResultTableUtils.getDataResultTable(Arrays.asList( - new DataResultColumnModel<>( - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), - (prog) -> { - return new DefaultCellModel(prog.getProgramName()) - .setTooltip(prog.getProgramPath()); - }, - 250), - new DataResultColumnModel<>( + this.topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + (prog) -> { + return new DefaultCellModel(prog.getProgramName()) + .setTooltip(prog.getProgramPath()); + }, + 250), + new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), (prog) -> { return new DefaultCellModel( @@ -97,7 +96,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan prog.getProgramName())); }, 150), - new DataResultColumnModel<>( + new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), (prog) -> { String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes()); @@ -105,7 +104,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan .setHorizontalAlignment(HorizontalAlign.RIGHT); }, 80), - new DataResultColumnModel<>( + new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(), (prog) -> { String date = prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun()); @@ -114,19 +113,17 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan }, 150) )); - this.topProgramsTable.setNoResultsMessage(Bundle.DataSourceSummaryUserActivityPanel_noDataExists()); // set up recent domains table - this.recentDomainsTable = DataResultTableUtils.getDataResultTable(Arrays.asList( - new DataResultColumnModel<>( - Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), - (d) -> new DefaultCellModel(d.getDomain()), - 250), - new DataResultColumnModel<>( + this.recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), + (d) -> new DefaultCellModel(d.getDomain()), + 250), + new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(), (d) -> new DefaultCellModel(d.getUrl()), 250), - new DataResultColumnModel<>( + new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(), (prog) -> { String lastVisit = prog.getLastVisit() == null ? "" : DATETIME_FORMAT.format(prog.getLastVisit()); @@ -135,16 +132,22 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan }, 150) )); - this.recentDomainsTable.setNoResultsMessage(Bundle.DataSourceSummaryUserActivityPanel_noDataExists()); + + this.tables = Arrays.asList( + topProgramsTable, + recentDomainsTable + ); // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchComponents>( (dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT), - topProgramsTable::setResult), + (result) -> topProgramsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.DataSourceSummaryUserActivityPanel_noDataExists())), new DataFetchComponents>( (dataSource) -> topDomainsData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT), - recentDomainsTable::setResult) + (result) -> recentDomainsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.DataSourceSummaryUserActivityPanel_noDataExists())) ); initComponents(); @@ -155,13 +158,12 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan // if no data source is present or the case is not open, // set results for tables to null. if (dataSource == null || !Case.isCaseOpen()) { - dataFetchComponents.forEach((item) -> item.getResultHandler() - .accept(DataLoadingResult.getLoadedResult(null))); + this.dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataFetchResult.getLoadedResult(null))); } else { // set tables to display loading screen - dataFetchComponents.forEach((item) -> item.getResultHandler() - .accept(DataLoadingResult.getLoadingResult())); + this.tables.forEach((table) -> table.showDefaultLoadingMessage()); // create swing workers to run for each table List> workers = dataFetchComponents diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED index 934dcb224a..332a884f05 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED @@ -1,2 +1,3 @@ -DataResultTable_errorMessage_defaultText=There was an error loading results. -DataResultTable_loadingMessage_defaultText=Loading results... +JTablePanel_errorMessage_defaultText=There was an error loading results. +JTablePanel_loadingMessage_defaultText=Loading results... +JTablePanel_noDataExists_defaultText=No data exists. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java similarity index 58% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java index 225dbf7382..26b76f50f3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataLoadingResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java @@ -21,35 +21,13 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** * The intermediate or end result of a loading process. */ -public final class DataLoadingResult { +public final class DataFetchResult { /** * The state of loading in the result. */ public enum ProcessorState { - LOADING, NOT_LOADED, LOADED, LOAD_ERROR - } - - // Since loading doesn't have any typed arguments, a static final instance is used. - private static final DataLoadingResult LOADING = new DataLoadingResult<>(ProcessorState.LOADING, null, null); - - // Since not loaded doesn't have any typed arguments, a static final instance is used. - private static final DataLoadingResult NOT_LOADED = new DataLoadingResult<>(ProcessorState.LOADED, null, null); - - /** - * @return Returns a data loading result. - */ - @SuppressWarnings("unchecked") - public static DataLoadingResult getLoadingResult() { - return (DataLoadingResult) LOADING; - } - - /** - * @return Returns a 'not loaded' result. - */ - @SuppressWarnings("unchecked") - public static DataLoadingResult getNotLoadedResult() { - return (DataLoadingResult) NOT_LOADED; + LOADED, LOAD_ERROR } /** @@ -59,8 +37,8 @@ public final class DataLoadingResult { * * @return The loaded data result. */ - public static DataLoadingResult getLoadedResult(R data) { - return new DataLoadingResult<>(ProcessorState.LOADED, data, null); + public static DataFetchResult getLoadedResult(R data) { + return new DataFetchResult<>(ProcessorState.LOADED, data, null); } /** @@ -70,13 +48,13 @@ public final class DataLoadingResult { * * @return */ - public static DataLoadingResult getLoadErrorResult(DataProcessorException e) { - return new DataLoadingResult<>(ProcessorState.LOAD_ERROR, null, e); + public static DataFetchResult getLoadErrorResult(DataFetcherException e) { + return new DataFetchResult<>(ProcessorState.LOAD_ERROR, null, e); } private final ProcessorState state; private final R data; - private final DataProcessorException exception; + private final DataFetcherException exception; /** * Main constructor for the DataLoadingResult. @@ -86,7 +64,7 @@ public final class DataLoadingResult { * result. * @param exception If the result is LOAD_ERROR, the related exception. */ - private DataLoadingResult(ProcessorState state, R data, DataProcessorException exception) { + private DataFetchResult(ProcessorState state, R data, DataFetcherException exception) { this.state = state; this.data = data; this.exception = exception; @@ -109,7 +87,7 @@ public final class DataLoadingResult { /** * @return The exception if the state is LOAD_ERROR. */ - public DataProcessorException getException() { + public DataFetcherException getException() { return exception; } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java index fda9f91cc1..b76579e44d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java @@ -37,8 +37,8 @@ public class DataFetchWorker extends SwingWorker { */ public static class DataFetchComponents { - private final DataProcessor processor; - private final Consumer> resultHandler; + private final DataFetcher processor; + private final Consumer> resultHandler; /** * Main constructor. @@ -48,7 +48,7 @@ public class DataFetchWorker extends SwingWorker { * @param resultHandler The result handler to be used as an argument for * the DataFetchWorker. */ - public DataFetchComponents(DataProcessor processor, Consumer> resultHandler) { + public DataFetchComponents(DataFetcher processor, Consumer> resultHandler) { this.processor = processor; this.resultHandler = resultHandler; } @@ -56,7 +56,7 @@ public class DataFetchWorker extends SwingWorker { /** * @return The function that processes or fetches the data. */ - public DataProcessor getProcessor() { + public DataFetcher getProcessor() { return processor; } @@ -64,7 +64,7 @@ public class DataFetchWorker extends SwingWorker { * @return When those results are received, this function handles * presenting the results in the UI. */ - public Consumer> getResultHandler() { + public Consumer> getResultHandler() { return resultHandler; } } @@ -72,8 +72,8 @@ public class DataFetchWorker extends SwingWorker { private static final Logger logger = Logger.getLogger(DataFetchWorker.class.getName()); private final A args; - private final DataProcessor processor; - private final Consumer> resultHandler; + private final DataFetcher processor; + private final Consumer> resultHandler; /** * Main constructor for this swing worker. @@ -96,8 +96,8 @@ public class DataFetchWorker extends SwingWorker { * @param args The args provided to the data processor. */ public DataFetchWorker( - DataProcessor processor, - Consumer> resultHandler, + DataFetcher processor, + Consumer> resultHandler, A args) { this.args = args; @@ -107,7 +107,7 @@ public class DataFetchWorker extends SwingWorker { @Override protected R doInBackground() throws Exception { - return processor.process(args); + return processor.runQuery(args); } @Override @@ -133,8 +133,8 @@ public class DataFetchWorker extends SwingWorker { // otherwise, there is an error to log logger.log(Level.WARNING, "There was an error while fetching results.", ex); - if (inner instanceof DataProcessorException) { - resultHandler.accept(DataLoadingResult.getLoadErrorResult((DataProcessorException) inner)); + if (inner instanceof DataFetcherException) { + resultHandler.accept(DataFetchResult.getLoadErrorResult((DataFetcherException) inner)); } return; } @@ -145,6 +145,6 @@ public class DataFetchWorker extends SwingWorker { } // if the data is loaded, send the data to the consumer. - resultHandler.accept(DataLoadingResult.getLoadedResult(result)); + resultHandler.accept(DataFetchResult.getLoadedResult(result)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java index 8f5b77f6d9..bd16912cb1 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java @@ -25,7 +25,7 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; * DataProcessorException in the event that the processing encountered an error. */ @FunctionalInterface -public interface DataProcessor { +public interface DataFetcher { /** * A function that accepts an input argument and outputs a result. Since it @@ -37,9 +37,9 @@ public interface DataProcessor { * * @return The output result. * - * @throws InterruptedException Thrown if the operation is cancelled. - * @throws DataProcessorException Thrown if there is an issue processing the - * request. + * @throws InterruptedException Thrown if the operation is cancelled. + * @throws DataFetcherException Thrown if there is an issue processing the + * request. */ - O process(I input) throws InterruptedException, DataProcessorException; + O runQuery(I input) throws InterruptedException, DataFetcherException; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java similarity index 87% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java index ef9477bfcf..b582aa078d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataProcessorException.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java @@ -22,7 +22,7 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; * An Exception that is thrown when there is an issue processing data in a * DataProcessor. */ -public class DataProcessorException extends Exception { +public class DataFetcherException extends Exception { private static final long serialVersionUID = 1L; @@ -31,7 +31,7 @@ public class DataProcessorException extends Exception { * * @param string The error message. */ - public DataProcessorException(String string) { + public DataFetcherException(String string) { super(string); } @@ -41,7 +41,7 @@ public class DataProcessorException extends Exception { * @param string The error message. * @param thrwbl The inner exception. */ - public DataProcessorException(String string, Throwable thrwbl) { + public DataFetcherException(String string, Throwable thrwbl) { super(string, thrwbl); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java deleted file mode 100644 index 514e2363a8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataResultTableUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit 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.datasourcesummary.uiutils; - -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.swing.table.TableColumnModel; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableColumn; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; - -/** - * Utility methods for instantiating aspects of a DataResultTable. - */ -public class DataResultTableUtils { - - /** - * Describes aspects of a column which can be used with getTableModel or - * getDataResultTable. 'T' represents the object that will represent rows in - * the table. - */ - public static class DataResultColumnModel { - - private final String headerTitle; - private final Function cellRenderer; - private final Integer width; - - /** - * Constructor for a DataResultColumnModel. - * - * @param headerTitle The title for the column. - * @param cellRenderer The method that generates a CellModel for the - * column based on the data. - */ - public DataResultColumnModel(String headerTitle, Function cellRenderer) { - this(headerTitle, cellRenderer, null); - } - - /** - * Constructor for a DataResultColumnModel. - * - * @param headerTitle The title for the column. - * @param cellRenderer The method that generates a CellModel for the - * column based on the data. - * @param width The preferred width of the column. - */ - public DataResultColumnModel(String headerTitle, Function cellRenderer, Integer width) { - this.headerTitle = headerTitle; - this.cellRenderer = cellRenderer; - this.width = width; - } - - /** - * @return The title for the column. - */ - public String getHeaderTitle() { - return headerTitle; - } - - /** - * @return The method that generates a CellModel for the column based on - * the data. - */ - public Function getCellRenderer() { - return cellRenderer; - } - - /** - * @return The preferred width of the column (can be null). - */ - public Integer getWidth() { - return width; - } - } - - private static final CellModelTableCellRenderer renderer = new CellModelTableCellRenderer(); - - /** - * Generates a TableColumnModel based on the column definitions. - * - * @param columns The column definitions. - * - * @return The corresponding TableColumnModel to be used with a JTable. - */ - public static TableColumnModel getTableColumnModel(List> columns) { - TableColumnModel tableModel = new DefaultTableColumnModel(); - - for (int i = 0; i < columns.size(); i++) { - TableColumn col = new TableColumn(i); - DataResultColumnModel model = columns.get(i); - // if a preferred width is specified in the column definition, - // set the underlying TableColumn preferred width. - if (model.getWidth() != null && model.getWidth() >= 0) { - col.setPreferredWidth(model.getWidth()); - } - - // set the title - col.setHeaderValue(model.getHeaderTitle()); - - // use the cell model renderer in this instance - col.setCellRenderer(renderer); - - tableModel.addColumn(col); - } - - return tableModel; - } - - /** - * Generates a ListTableModel based on the column definitions provided where - * 'T' is the object representing each row. - * - * @param columns The column definitions. - * - * @return The corresponding ListTableModel. - */ - public static ListTableModel getTableModel(List> columns) { - List> columnRenderers = columns.stream() - .map((colModel) -> colModel.getCellRenderer()) - .collect(Collectors.toList()); - - return new DefaultListTableModel(columnRenderers); - } - - /** - * Generates a DataResultTable corresponding to the provided column - * definitions where 'T' is the object representing each row. - * - * @param columns The column definitions. - * - * @return The corresponding DataResultTable. - */ - public static JTablePanel getDataResultTable(List> columns) { - ListTableModel tableModel = getTableModel(columns); - JTablePanel resultTable = new JTablePanel<>(tableModel); - return resultTable.setColumnModel(getTableColumnModel(columns)); - } - - private DataResultTableUtils() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java index d1e940837a..ed472230c2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultListTableModel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Function; @@ -52,7 +53,7 @@ public class DefaultListTableModel extends AbstractTableModel implements List @Override public void setDataRows(List dataRows) { - this.dataRows = dataRows == null ? Collections.emptyList() : Collections.unmodifiableList(dataRows); + this.dataRows = dataRows == null ? Collections.emptyList() : new ArrayList<>(dataRows); super.fireTableDataChanged(); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index ad3c7cd930..c74c5d65c9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -22,7 +22,9 @@ import java.awt.BorderLayout; import java.awt.Graphics; import java.util.Collections; import java.util.List; +import java.util.function.Function; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JLayer; @@ -30,18 +32,21 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.plaf.LayerUI; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; /** * A table that displays a list of items and also can display messages for * loading, load error, and not loaded. */ @Messages({ - "DataResultTable_loadingMessage_defaultText=Loading results...", - "DataResultTable_errorMessage_defaultText=There was an error loading results." -}) + "JTablePanel_loadingMessage_defaultText=Loading results...", + "JTablePanel_errorMessage_defaultText=There was an error loading results.", + "JTablePanel_noDataExists_defaultText=No data exists.",}) public class JTablePanel extends JPanel { /** @@ -54,17 +59,17 @@ public class JTablePanel extends JPanel { private static final long serialVersionUID = 1L; - private final JLabel child; + private final JLabel label; private boolean visible; /** * Main constructor for the Overlay. */ Overlay() { - child = new JLabel(); - child.setHorizontalAlignment(JLabel.CENTER); - child.setVerticalAlignment(JLabel.CENTER); - child.setOpaque(false); + label = new JLabel(); + label.setHorizontalAlignment(JLabel.CENTER); + label.setVerticalAlignment(JLabel.CENTER); + label.setOpaque(false); } @@ -88,8 +93,8 @@ public class JTablePanel extends JPanel { /** * @return The child JLabel component. */ - JLabel getChild() { - return child; + JLabel getLabel() { + return label; } /** @@ -98,7 +103,8 @@ public class JTablePanel extends JPanel { * @param message The message to be displayed. */ void setMessage(String message) { - child.setText(String.format("
%s
", message)); + label.setText(String.format("
%s
", + message == null ? "" : message)); } @Override @@ -114,8 +120,67 @@ public class JTablePanel extends JPanel { int h = c.getHeight(); // paint the jlabel if visible. - child.setBounds(0, 0, w, h); - child.paint(g); + label.setBounds(0, 0, w, h); + label.paint(g); + } + } + + /** + * Describes aspects of a column which can be used with getTableModel or + * getJTablePanel. 'T' represents the object that will represent rows in the + * table. + */ + public static class ColumnModel { + + private final String headerTitle; + private final Function cellRenderer; + private final Integer width; + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the + * column based on the data. + */ + public ColumnModel(String headerTitle, Function cellRenderer) { + this(headerTitle, cellRenderer, null); + } + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the + * column based on the data. + * @param width The preferred width of the column. + */ + public ColumnModel(String headerTitle, Function cellRenderer, Integer width) { + this.headerTitle = headerTitle; + this.cellRenderer = cellRenderer; + this.width = width; + } + + /** + * @return The title for the column. + */ + public String getHeaderTitle() { + return headerTitle; + } + + /** + * @return The method that generates a CellModel for the column based on + * the data. + */ + public Function getCellRenderer() { + return cellRenderer; + } + + /** + * @return The preferred width of the column (can be null). + */ + public Integer getWidth() { + return width; } } @@ -123,21 +188,92 @@ public class JTablePanel extends JPanel { private static final Logger logger = Logger.getLogger(JTablePanel.class.getName()); - private static final String DEFAULT_LOADING_MESSAGE = Bundle.DataResultTable_loadingMessage_defaultText(); - private static final String DEFAULT_ERROR_MESSAGE = Bundle.DataResultTable_errorMessage_defaultText(); - private static final String DEFAULT_NO_RESULTS_MESSAGE = ""; - private static final String DEFAULT_NOT_LOADED_MESSAGE = ""; + private static final String DEFAULT_LOADING_MESSAGE = Bundle.JTablePanel_loadingMessage_defaultText(); + private static final String DEFAULT_ERROR_MESSAGE = Bundle.JTablePanel_errorMessage_defaultText(); + private static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.JTablePanel_noDataExists_defaultText(); + + private static final CellModelTableCellRenderer DEFAULT_CELL_RENDERER = new CellModelTableCellRenderer(); + + /** + * Generates a TableColumnModel based on the column definitions. + * + * @param columns The column definitions. + * + * @return The corresponding TableColumnModel to be used with a JTable. + */ + public static TableColumnModel getTableColumnModel(List> columns) { + TableColumnModel tableModel = new DefaultTableColumnModel(); + + for (int i = 0; i < columns.size(); i++) { + TableColumn col = new TableColumn(i); + ColumnModel model = columns.get(i); + // if a preferred width is specified in the column definition, + // set the underlying TableColumn preferred width. + if (model.getWidth() != null && model.getWidth() >= 0) { + col.setPreferredWidth(model.getWidth()); + } + + // set the title + col.setHeaderValue(model.getHeaderTitle()); + + // use the cell model renderer in this instance + col.setCellRenderer(DEFAULT_CELL_RENDERER); + + tableModel.addColumn(col); + } + + return tableModel; + } + + /** + * Generates a ListTableModel based on the column definitions provided where + * 'T' is the object representing each row. + * + * @param columns The column definitions. + * + * @return The corresponding ListTableModel. + */ + public static ListTableModel getTableModel(List> columns) { + List> columnRenderers = columns.stream() + .map((colModel) -> colModel.getCellRenderer()) + .collect(Collectors.toList()); + + return new DefaultListTableModel(columnRenderers); + } + + /** + * Generates a JTablePanel corresponding to the provided column definitions + * where 'T' is the object representing each row. + * + * @param columns The column definitions. + * + * @return The corresponding JTablePanel. + */ + public static JTablePanel getJTablePanel(List> columns) { + ListTableModel tableModel = getTableModel(columns); + JTablePanel resultTable = new JTablePanel<>(tableModel); + return resultTable.setColumnModel(getTableColumnModel(columns)); + } + + /** + * @return The default error message. + */ + public static String getDefaultErrorMessage() { + return DEFAULT_ERROR_MESSAGE; + } + + /** + * @return The default message for no results. + */ + public static String getDefaultNoResultsMessage() { + return DEFAULT_NO_RESULTS_MESSAGE; + } private final JScrollPane tableScrollPane; private final Overlay overlayLayer; private final ListTableModel tableModel; private final JTable table; - private String loadingMessage = DEFAULT_LOADING_MESSAGE; - private String errorMessage = DEFAULT_ERROR_MESSAGE; - private String noResultsMessage = DEFAULT_NO_RESULTS_MESSAGE; - private String notLoadedMessage = DEFAULT_NOT_LOADED_MESSAGE; - /** * Main constructor. * @@ -175,93 +311,15 @@ public class JTablePanel extends JPanel { } /** - * @return The message shown when loading. - */ - public String getLoadingMessage() { - return loadingMessage; - } - - /** - * @return The message shown when there is an exception. - */ - public String getErrorMessage() { - return errorMessage; - } - - /** - * @return The message shown when there are no results. - */ - public String getNoResultsMessage() { - return noResultsMessage; - } - - /** - * @return The message shown when the table has not been loaded. - */ - public String getNotLoadedMessage() { - return notLoadedMessage; - } - - /** - * Sets the loading message. - * - * @param loadingMessage The loading message. - * - * @return As a utility, returns this. - */ - public JTablePanel setLoadingMessage(String loadingMessage) { - this.loadingMessage = loadingMessage; - return this; - } - - /** - * Sets the error message - * - * @param errorMessage The error message. - * - * @return As a utility, returns this. - */ - public JTablePanel setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - return this; - } - - /** - * Sets the message to be shown when no results are present. - * - * @param noResultsMessage The 'no results' message. - * - * @return As a utility, returns this. - */ - public JTablePanel setNoResultsMessage(String noResultsMessage) { - this.noResultsMessage = noResultsMessage; - return this; - } - - /** - * Sets the 'not loaded' message. - * - * @param notLoadedMessage The message to be shown when results are not - * loaded. - * - * @return As a utility, returns this. - */ - public JTablePanel setNotLoadedMessage(String notLoadedMessage) { - this.notLoadedMessage = notLoadedMessage; - return this; - } - - /** - * Sets the data to be shown in the JTable. + * Sets the data to be shown in the JTable. Repaint is not handled in this + * method and should be handled separately. * * @param data The list of data objects to be shown. */ private void setResultList(List data) { // set the list of data to be shown as either the data or an empty list // on null. - List dataToSet = (data != null) - ? Collections.unmodifiableList(data) - : Collections.emptyList(); + List dataToSet = (data == null) ? Collections.emptyList() : data; // since the data is being reset, scroll to the top. tableScrollPane.getVerticalScrollBar().setValue(0); @@ -271,7 +329,8 @@ public class JTablePanel extends JPanel { } /** - * Sets the message and visibility of the overlay. + * Sets the message and visibility of the overlay. Repaint is not handled in + * this method and should be handled separately. * * @param visible The visibility of the overlay. * @param message The message in the overlay. @@ -282,59 +341,86 @@ public class JTablePanel extends JPanel { } /** - * Sets the result to be displayed. + * Clears the results from the underlying JTable and shows the provided + * message. * - * @param result The loading result to be displayed. - * - * @return As a utility, returns this. + * @param message The message to be shown. */ - public JTablePanel setResult(DataLoadingResult> result) { + public synchronized void showMessage(String message) { + setResultList(null); + setOverlay(true, message); + repaint(); + } + + /** + * Shows a default loading message on the table. This will clear any results + * in the table. + */ + public void showDefaultLoadingMessage() { + showMessage(DEFAULT_LOADING_MESSAGE); + } + + /** + * Shows the list as rows of data in the table. If overlay message will be + * cleared if present. + * + * @param data The data to be shown where each item represents a row of + * data. + */ + public synchronized void showResults(List data) { + setOverlay(false, null); + setResultList(data); + repaint(); + } + + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the errorMessage will be displayed. If the operation completed + * successfully and no data is present, noResultsMessage will be shown. + * Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + * @param errorMessage The error message to be shown in the event of an + * error. + * @param noResultsMessage The message to be shown if there are no results + * but the operation completed successfully. + */ + public void showDataFetchResult(DataFetchResult> result, String errorMessage, String noResultsMessage) { if (result == null) { logger.log(Level.SEVERE, "Null data processor result received."); - return this; + return; } switch (result.getState()) { case LOADED: - if (result.getData() == null || result.getData().size() <= 0) { - // if loaded and there is no data, set an empty result list - // and set the overlay to show no results message. - setResultList(null); - setOverlay(true, getNoResultsMessage()); + if (result.getData() == null || result.getData().isEmpty()) { + showMessage(noResultsMessage); } else { - // otherwise show the data and remove the overlay. - setResultList(result.getData()); - setOverlay(false, null); + showResults(result.getData()); } break; - case NOT_LOADED: - // if results are not loaded, empty results list and display - // not loaded message - setResultList(null); - setOverlay(true, getNotLoadedMessage()); - break; - case LOADING: - // if this table is loading, then set the results to empty and - // display the loading message. - setResultList(null); - setOverlay(true, getLoadingMessage()); - break; case LOAD_ERROR: // if there is an error, log accordingly, set result list to // empty and display error message logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException()); - setResultList(null); - setOverlay(true, getErrorMessage()); + showMessage(errorMessage); break; default: // an unknown loading state was specified. log accordingly. logger.log(Level.SEVERE, "No known loading state was found in result."); break; } + } - // repaint to capture changes. - repaint(); - - return this; + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation + * completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE + * will be shown. Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + */ + public void showDataFetchResult(DataFetchResult> result) { + showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java index e25b53a3ad..bf5f1d1552 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ListTableModel.java @@ -22,8 +22,8 @@ import java.util.List; import javax.swing.table.TableModel; /** - * An interface to be used with the DataResultTable that specifies a TableModel - * to be used with the underlying JTable based on a list of object type T. + * An interface to be used with the JTablePanel that specifies a TableModel to + * be used with the underlying JTable based on a list of object type T. */ public interface ListTableModel extends TableModel { From 47d200d19045796a46a039122d7c14547f314eca Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 14:31:43 -0400 Subject: [PATCH 15/16] address codacy remark --- .../datasourcesummary/datamodel/SleuthkitCaseProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java index 9c5928d6b2..8371b2fefd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java @@ -35,7 +35,7 @@ public interface SleuthkitCaseProvider { * Exception thrown in the event that the SleuthkitCase object cannot be * provided. */ - static class SleuthkitCaseProviderException extends Exception { + class SleuthkitCaseProviderException extends Exception { private static final long serialVersionUID = 1L; From dd5f2b4e40300ad53c949ee2654e174493cff67f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 Aug 2020 19:26:18 -0400 Subject: [PATCH 16/16] revisions based on feedback --- .../ui/BaseDataSourceSummaryPanel.java | 10 +--- .../DataSourceSummaryUserActivityPanel.java | 2 +- .../uiutils/DataFetchResult.java | 40 ++++++++-------- .../uiutils/DataFetchWorker.java | 40 ++++++++-------- .../uiutils/DataFetcher.java | 16 +++---- .../uiutils/DataFetcherException.java | 47 ------------------- .../uiutils/JTablePanel.java | 13 ++--- 7 files changed, 53 insertions(+), 115 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index dc31c9a77f..9a0cddd05a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -34,15 +34,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor(); private DataSource dataSource; - /** - * The datasource currently used as the model in this panel. - * - * @return The datasource currently being used as the model in this panel. - */ - synchronized DataSource getDataSource() { - return dataSource; - } - /** * Sets datasource to visualize in the panel. * @@ -52,6 +43,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { DataSource oldDataSource = this.dataSource; this.dataSource = dataSource; if (this.dataSource != oldDataSource) { + this.executor.cancelRunning(); onNewDataSource(this.dataSource); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index 393c429c46..eb3f09a81f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -159,7 +159,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan // set results for tables to null. if (dataSource == null || !Case.isCaseOpen()) { this.dataFetchComponents.forEach((item) -> item.getResultHandler() - .accept(DataFetchResult.getLoadedResult(null))); + .accept(DataFetchResult.getSuccessResult(null))); } else { // set tables to display loading screen diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java index 26b76f50f3..93cc24f5fe 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchResult.java @@ -19,52 +19,52 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** - * The intermediate or end result of a loading process. + * The result of a loading process. */ public final class DataFetchResult { /** - * The state of loading in the result. + * The type of result. */ - public enum ProcessorState { - LOADED, LOAD_ERROR + public enum ResultType { + SUCCESS, ERROR } /** - * Creates a DataLoadingResult of loaded data including the data. + * Creates a DataFetchResult of loaded data including the data. * * @param data The data. * * @return The loaded data result. */ - public static DataFetchResult getLoadedResult(R data) { - return new DataFetchResult<>(ProcessorState.LOADED, data, null); + public static DataFetchResult getSuccessResult(R data) { + return new DataFetchResult<>(ResultType.SUCCESS, data, null); } /** - * Returns a load error result. + * Returns an error result. * * @param e The exception (if any) present with the error. * - * @return + * @return The error result. */ - public static DataFetchResult getLoadErrorResult(DataFetcherException e) { - return new DataFetchResult<>(ProcessorState.LOAD_ERROR, null, e); + public static DataFetchResult getErrorResult(Throwable e) { + return new DataFetchResult<>(ResultType.ERROR, null, e); } - private final ProcessorState state; + private final ResultType state; private final R data; - private final DataFetcherException exception; + private final Throwable exception; /** * Main constructor for the DataLoadingResult. * * @param state The state of the result. - * @param data If the result is LOADED, the data related to this + * @param data If the result is SUCCESS, the data related to this * result. - * @param exception If the result is LOAD_ERROR, the related exception. + * @param exception If the result is ERROR, the related exception. */ - private DataFetchResult(ProcessorState state, R data, DataFetcherException exception) { + private DataFetchResult(ResultType state, R data, Throwable exception) { this.state = state; this.data = data; this.exception = exception; @@ -73,21 +73,21 @@ public final class DataFetchResult { /** * @return The current loading state. */ - public ProcessorState getState() { + public ResultType getResultType() { return state; } /** - * @return The data if the state is LOADED. + * @return The data if the state is SUCCESS. */ public R getData() { return data; } /** - * @return The exception if the state is LOAD_ERROR. + * @return The exception if the state is ERROR. */ - public DataFetcherException getException() { + public Throwable getException() { return exception; } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java index b76579e44d..89f11f544c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java @@ -25,39 +25,40 @@ import javax.swing.SwingWorker; import org.sleuthkit.autopsy.coreutils.Logger; /** - * A Swing worker that accepts an argument of a data processor and a result - * handler. + * A Swing worker that accepts an argument of a data fetcher and a result + * handler. If the data fetcher throws an InterruptedException, it is treated as + * a cancellation and not passed to the result handler. */ public class DataFetchWorker extends SwingWorker { /** - * Holds the functions necessary for a DataFetchWorker. Includes the - * processor and result handler. The args are not included since they are - * likely dynamic. + * Holds the functions necessary for a DataFetchWorker. Includes the fetcher + * and result handler. The args are not included since they are likely + * dynamic. */ public static class DataFetchComponents { - private final DataFetcher processor; + private final DataFetcher fetcher; private final Consumer> resultHandler; /** * Main constructor. * - * @param processor The processor to be used as an argument for the + * @param fetcher The fetcher to be used as an argument for the * DataFetchWorker. * @param resultHandler The result handler to be used as an argument for * the DataFetchWorker. */ - public DataFetchComponents(DataFetcher processor, Consumer> resultHandler) { - this.processor = processor; + public DataFetchComponents(DataFetcher fetcher, Consumer> resultHandler) { + this.fetcher = fetcher; this.resultHandler = resultHandler; } /** - * @return The function that processes or fetches the data. + * @return The function that fetches the data. */ - public DataFetcher getProcessor() { - return processor; + public DataFetcher getFetcher() { + return fetcher; } /** @@ -83,14 +84,16 @@ public class DataFetchWorker extends SwingWorker { * @param args The argument to be provided to the data processor. */ public DataFetchWorker(DataFetchComponents components, A args) { - this(components.getProcessor(), components.getResultHandler(), args); + this(components.getFetcher(), components.getResultHandler(), args); } /** * Main constructor for this swing worker. * - * @param processor The function that will do the processing of the data - * provided the given args. + * @param processor The function that will do the fetching of the data + * provided the given args. InterruptedException's are + * treated as cancellations and are not passed to the + * result handler. * @param resultHandler The ui function that will handle the result of the * data processing. * @param args The args provided to the data processor. @@ -133,9 +136,8 @@ public class DataFetchWorker extends SwingWorker { // otherwise, there is an error to log logger.log(Level.WARNING, "There was an error while fetching results.", ex); - if (inner instanceof DataFetcherException) { - resultHandler.accept(DataFetchResult.getLoadErrorResult((DataFetcherException) inner)); - } + // and pass the result to the client + resultHandler.accept(DataFetchResult.getErrorResult(inner)); return; } @@ -145,6 +147,6 @@ public class DataFetchWorker extends SwingWorker { } // if the data is loaded, send the data to the consumer. - resultHandler.accept(DataFetchResult.getLoadedResult(result)); + resultHandler.accept(DataFetchResult.getSuccessResult(result)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java index bd16912cb1..6eabe79634 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java @@ -21,25 +21,23 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; /** * A function that accepts input of type I and outputs type O. This function is * meant to be utilized with DataFetchWorker and can therefore, throw an - * interrupted exception if the processing is cancelled or a - * DataProcessorException in the event that the processing encountered an error. + * interrupted exception if the processing is cancelled or an Exception of on + * another type in the event that the fetching encountered an error. */ @FunctionalInterface public interface DataFetcher { /** * A function that accepts an input argument and outputs a result. Since it - * is meant to be used with the DataFetchWorker, it throws an interrupted - * exception if the thread has been interrupted. It throws a data processing - * exception if there is an error during processing. + * is meant to be used with the DataFetchWorker, it may throw an interrupted + * exception if the thread has been interrupted. It throws another type of + * exception if there is an error during fetching. * * @param input The input argument. * * @return The output result. * - * @throws InterruptedException Thrown if the operation is cancelled. - * @throws DataFetcherException Thrown if there is an issue processing the - * request. + * @throws Exception */ - O runQuery(I input) throws InterruptedException, DataFetcherException; + O runQuery(I input) throws Exception; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java deleted file mode 100644 index b582aa078d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcherException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit 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.datasourcesummary.uiutils; - -/** - * An Exception that is thrown when there is an issue processing data in a - * DataProcessor. - */ -public class DataFetcherException extends Exception { - - private static final long serialVersionUID = 1L; - - /** - * Main constructor. - * - * @param string The error message. - */ - public DataFetcherException(String string) { - super(string); - } - - /** - * Main constructor. - * - * @param string The error message. - * @param thrwbl The inner exception. - */ - public DataFetcherException(String string, Throwable thrwbl) { - super(string, thrwbl); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index c74c5d65c9..1e4184d85b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -90,13 +90,6 @@ public class JTablePanel extends JPanel { this.visible = visible; } - /** - * @return The child JLabel component. - */ - JLabel getLabel() { - return label; - } - /** * Sets the message to be displayed in the child jlabel. * @@ -391,15 +384,15 @@ public class JTablePanel extends JPanel { return; } - switch (result.getState()) { - case LOADED: + switch (result.getResultType()) { + case SUCCESS: if (result.getData() == null || result.getData().isEmpty()) { showMessage(noResultsMessage); } else { showResults(result.getData()); } break; - case LOAD_ERROR: + case ERROR: // if there is an error, log accordingly, set result list to // empty and display error message logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException());