Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DataResultPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.corecomponents;
20 
21 import java.awt.Cursor;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.List;
28 import javax.swing.JTabbedPane;
29 import javax.swing.SwingUtilities;
30 import javax.swing.event.ChangeEvent;
31 import javax.swing.event.ChangeListener;
32 import org.openide.explorer.ExplorerManager;
33 import org.openide.nodes.Node;
34 import org.openide.nodes.NodeEvent;
35 import org.openide.nodes.NodeListener;
36 import org.openide.nodes.NodeMemberEvent;
37 import org.openide.nodes.NodeReorderEvent;
38 import org.openide.util.Lookup;
39 import org.openide.util.NbBundle;
46 
75 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
76 public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener, ExplorerManager.Provider {
77 
78  private static final long serialVersionUID = 1L;
79  private static final int NO_TAB_SELECTED = -1;
80  private static final String PLEASE_WAIT_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.pleasewaitNodeDisplayName");
81  private final boolean isMain;
82  private final List<DataResultViewer> resultViewers;
86  private ExplorerManager explorerManager;
87  private Node currentRootNode;
88  private boolean listeningToTabbedPane;
89 
107  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount) {
108  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), DataContentTopComponent.findInstance());
109  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
110  resultPanel.open();
111  return resultPanel;
112  }
113 
134  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, Collection<DataResultViewer> viewers) {
135  DataResultPanel resultPanel = new DataResultPanel(title, false, viewers, DataContentTopComponent.findInstance());
136  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
137  resultPanel.open();
138  return resultPanel;
139  }
140 
163  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView) {
164  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), customContentView);
165  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
166  resultPanel.open();
167  return resultPanel;
168  }
169 
189  public static DataResultPanel createInstanceUninitialized(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView) {
190  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), customContentView);
191  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
192  return resultPanel;
193  }
194 
206  private static void createInstanceCommon(String title, String description, Node currentRootNode, int childNodeCount, DataResultPanel resultViewPanel) {
207  resultViewPanel.setTitle(title);
208  resultViewPanel.setName(title);
209  resultViewPanel.setNumberOfChildNodes(childNodeCount);
210  resultViewPanel.setNode(currentRootNode);
211  resultViewPanel.setPath(description);
212  }
213 
231  DataResultPanel(String title, boolean isMain, Collection<DataResultViewer> viewers, DataContent contentView) {
232  this.setTitle(title);
233  this.isMain = isMain;
234  this.contentView = contentView;
235  this.resultViewers = new ArrayList<>(viewers);
236  this.explorerManagerListener = new ExplorerManagerListener();
237  this.rootNodeListener = new RootNodeListener();
238  initComponents();
239  }
240 
247  @Override
248  public String getPreferredID() {
249  return getName();
250  }
251 
257  @Override
258  public void setTitle(String title) {
259  setName(title);
260  }
261 
268  @Override
269  public void setPath(String description) {
270  this.descriptionLabel.setText(description);
271  }
272 
278  public void addResultViewer(DataResultViewer resultViewer) {
279  resultViewers.add(resultViewer);
280  resultViewerTabs.addTab(resultViewer.getTitle(), resultViewer.getComponent());
281  }
282 
288  @Override
289  public List<DataResultViewer> getViewers() {
290  return Collections.unmodifiableList(resultViewers);
291  }
292 
300  public void setContentViewer(DataContent customContentView) {
301  this.contentView = customContentView;
302  }
303 
308  public void open() {
309  /*
310  * The parent top component is expected to be an explorer manager
311  * provider that exposes a lookup maintained by its explorer manager to
312  * the actions global context. The child result view panel will then
313  * find the parent top component's explorer manager at runtime, so that
314  * it can act as an explorer manager provider for its child result
315  * viewers. This connects the nodes displayed in the result viewers to
316  * the actions global context.
317  */
318  if (this.explorerManager == null) {
319  this.explorerManager = ExplorerManager.find(this);
320  this.explorerManager.addPropertyChangeListener(this.explorerManagerListener);
321  }
322 
323  /*
324  * Load either the supplied result viewers or the result viewers
325  * provided by the result viewer extension point into the tabbed pane.
326  * If loading from the extension point and distinct result viewer
327  * instances MUST be created if this is not the "main" result view.
328  */
329  if (this.resultViewerTabs.getTabCount() == 0) {
330  if (this.resultViewers.isEmpty()) {
331  for (DataResultViewer resultViewer : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
332  if (this.isMain) {
333  this.resultViewers.add(resultViewer);
334  } else {
335  this.resultViewers.add(resultViewer.createInstance());
336  }
337  }
338  }
339  this.resultViewers.forEach((resultViewer) -> resultViewerTabs.addTab(resultViewer.getTitle(), resultViewer.getComponent()));
340  }
341 
342  this.setVisible(true);
343  }
344 
355  @Override
356  public void setNode(Node rootNode) {
357  if (this.currentRootNode != null) {
358  this.currentRootNode.removeNodeListener(rootNodeListener);
359  }
360 
361  /*
362  * Deferring becoming a listener to the tabbed pane until this point
363  * eliminates handling a superfluous stateChanged event during
364  * construction.
365  */
366  if (listeningToTabbedPane == false) {
367  resultViewerTabs.addChangeListener(this);
368  listeningToTabbedPane = true;
369  }
370 
371  this.currentRootNode = rootNode;
372  if (this.currentRootNode != null) {
373  /*
374  * The only place we reset the rootNodeListener allowing the
375  * contents of the results tab represented by this node to be
376  * changed a single time before it is necessary to reset it again.
377  * Necessary when transitioning from "Please wait..." node to having
378  * contents.
379  */
380  rootNodeListener.reset();
381  this.currentRootNode.addNodeListener(rootNodeListener);
382  }
383 
384  this.resultViewers.forEach((viewer) -> {
385  viewer.resetComponent();
386  });
387  setupTabs(this.currentRootNode);
388 
389  if (this.currentRootNode != null) {
390  int childrenCount = this.currentRootNode.getChildren().getNodesCount();
391  this.numberOfChildNodesLabel.setText(Integer.toString(childrenCount));
392  }
393  this.numberOfChildNodesLabel.setVisible(true);
394  }
395 
403  public Node getRootNode() {
404  return currentRootNode;
405  }
406 
413  public void setNumberOfChildNodes(Integer numberOfChildNodes) {
414  this.numberOfChildNodesLabel.setText(Integer.toString(numberOfChildNodes));
415  }
416 
423  public void setSelectedNodes(Node[] selectedNodes) {
424  this.resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
425  }
426 
433  private void setupTabs(Node selectedNode) {
434  /*
435  * Enable or disable the result viewer tabs based on whether or not the
436  * corresponding results viewer supports display of the selected node.
437  */
438  for (int i = 0; i < resultViewerTabs.getTabCount(); i++) {
439  if (resultViewers.get(i).isSupported(selectedNode)) {
440  resultViewerTabs.setEnabledAt(i, true);
441  } else {
442  resultViewerTabs.setEnabledAt(i, false);
443  }
444  }
445 
446  /*
447  * If the selected node has a child to be selected, default the selected
448  * tab to the table result viewer. Otherwise, use the last selected tab,
449  * if it is enabled. If not, select the first enabled tab that can be
450  * found.
451  */
452  int tabToSelect = NO_TAB_SELECTED;
453  if (selectedNode instanceof TableFilterNode) {
454  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) selectedNode).getChildNodeSelectionInfo();
455  if (null != selectedChildInfo) {
456  for (int i = 0; i < resultViewers.size(); ++i) {
457  if (resultViewers.get(i) instanceof DataResultViewerTable && resultViewerTabs.isEnabledAt(i)) {
458  tabToSelect = i;
459  }
460  }
461  }
462  }
463  if (tabToSelect == NO_TAB_SELECTED) {
464  if ((tabToSelect == NO_TAB_SELECTED) || (!resultViewerTabs.isEnabledAt(tabToSelect))) {
465  for (int i = 0; i < resultViewerTabs.getTabCount(); ++i) {
466  if (resultViewerTabs.isEnabledAt(i)) {
467  tabToSelect = i;
468  break;
469  }
470  }
471  }
472  }
473 
474  /*
475  * If there is a tab to select, do so, and push the selected node to the
476  * corresponding result viewer.
477  */
478  if (tabToSelect != NO_TAB_SELECTED) {
479  resultViewerTabs.setSelectedIndex(tabToSelect);
480  resultViewers.get(tabToSelect).setNode(selectedNode);
481  }
482  }
483 
490  @Override
491  public void stateChanged(ChangeEvent event) {
492  JTabbedPane pane = (JTabbedPane) event.getSource();
493  int currentTab = pane.getSelectedIndex();
494  if (currentTab != DataResultPanel.NO_TAB_SELECTED) {
495  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
496  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
497  try {
498  currentViewer.setNode(currentRootNode);
499  } finally {
500  this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
501  }
502  }
503  }
504 
511  public boolean canClose() {
512  /*
513  * If this is the "main" panel, only allow it to be closed when no case
514  * is open or no there are no data sources in the current case.
515  */
516  Case openCase;
517  try {
518  openCase = Case.getCurrentCaseThrows();
519  } catch (NoCurrentCaseException ex) {
520  return true;
521  }
522  return (!this.isMain) || openCase.hasData() == false;
523  }
524 
529  void close() {
530  if (explorerManager != null && explorerManagerListener != null) {
531  explorerManager.removePropertyChangeListener(explorerManagerListener);
532  explorerManager = null;
533  }
534 
535  this.resultViewers.forEach((viewer) -> viewer.setNode(null));
536 
537  if (!this.isMain) {
538  this.resultViewers.forEach(DataResultViewer::clearComponent);
539  this.descriptionLabel.removeAll();
540  this.numberOfChildNodesLabel.removeAll();
541  this.matchLabel.removeAll();
542  this.setLayout(null);
543  this.removeAll();
544  this.setVisible(false);
545  }
546  }
547 
548  @Override
549  public ExplorerManager getExplorerManager() {
550  return explorerManager;
551 
552  }
553 
564  private class ExplorerManagerListener implements PropertyChangeListener {
565 
566  @Override
567  public void propertyChange(PropertyChangeEvent evt) {
568  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES) && contentView != null) {
569  /*
570  * Pass a single node selection in a result viewer to the
571  * content view. Note that passing null to the content view
572  * signals that either multiple nodes are selected, or a
573  * previous selection has been cleared. This is important to the
574  * content view, since its child content viewers only work for a
575  * single node.
576  */
577  Node[] selectedNodes = explorerManager.getSelectedNodes();
578  if (selectedNodes.length == 1) {
579  contentView.setNode(selectedNodes[0]);
580  } else {
581  contentView.setNode(null);
582  }
583  }
584  }
585  }
586 
593  private class RootNodeListener implements NodeListener {
594 
595  //it is assumed we are still waiting for data when the node is initially constructed
596  private volatile boolean waitingForData = true;
597 
598  public void reset() {
599  waitingForData = true;
600  }
601 
602  @Override
603  public void childrenAdded(final NodeMemberEvent nme) {
604  Node[] delta = nme.getDelta();
605  updateMatches();
606 
607  /*
608  * Ensures that after the initial call to setupTabs in the
609  * DataResultPanel.setNode method that we only call setupTabs one
610  * additional time. This is to account for the transition that is
611  * possible from a "Please wait..." node or a tab with no results in
612  * it and a tab containing data and thereby having all of it's
613  * columns.
614  */
615  if (waitingForData && containsReal(delta)) {
616  waitingForData = false;
617  if (SwingUtilities.isEventDispatchThread()) {
618  setupTabs(nme.getNode());
619  } else {
620  SwingUtilities.invokeLater(() -> {
621  setupTabs(nme.getNode());
622  });
623  }
624  }
625  }
626 
627  private boolean containsReal(Node[] delta) {
628  for (Node n : delta) {
629  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
630  return true;
631  }
632  }
633  return false;
634  }
635 
640  private void updateMatches() {
641  if (currentRootNode != null && currentRootNode.getChildren() != null) {
642  setNumMatches(currentRootNode.getChildren().getNodesCount());
643  }
644  }
645 
646  @Override
647  public void childrenRemoved(NodeMemberEvent nme) {
648  updateMatches();
649  }
650 
651  @Override
652  public void childrenReordered(NodeReorderEvent nre) {
653  }
654 
655  @Override
656  public void nodeDestroyed(NodeEvent ne) {
657  }
658 
659  @Override
660  public void propertyChange(PropertyChangeEvent evt) {
661  }
662  }
663 
669  @SuppressWarnings("unchecked")
670  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
671  private void initComponents() {
672 
673  descriptionLabel = new javax.swing.JLabel();
674  numberOfChildNodesLabel = new javax.swing.JLabel();
675  matchLabel = new javax.swing.JLabel();
676  resultViewerTabs = new javax.swing.JTabbedPane();
677 
678  setMinimumSize(new java.awt.Dimension(0, 5));
679  setPreferredSize(new java.awt.Dimension(5, 5));
680 
681  org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.descriptionLabel.text")); // NOI18N
682  descriptionLabel.setMinimumSize(new java.awt.Dimension(5, 14));
683 
684  org.openide.awt.Mnemonics.setLocalizedText(numberOfChildNodesLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberOfChildNodesLabel.text")); // NOI18N
685 
686  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
687 
688  resultViewerTabs.setMinimumSize(new java.awt.Dimension(0, 5));
689 
690  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
691  this.setLayout(layout);
692  layout.setHorizontalGroup(
693  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
694  .addGroup(layout.createSequentialGroup()
695  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
696  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
697  .addComponent(numberOfChildNodesLabel)
698  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
699  .addComponent(matchLabel))
700  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
701  );
702  layout.setVerticalGroup(
703  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
704  .addGroup(layout.createSequentialGroup()
705  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
706  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
707  .addComponent(numberOfChildNodesLabel)
708  .addComponent(matchLabel))
709  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
710  .addGap(0, 0, 0)
711  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
712  );
713  }// </editor-fold>//GEN-END:initComponents
714  // Variables declaration - do not modify//GEN-BEGIN:variables
715  private javax.swing.JLabel descriptionLabel;
716  private javax.swing.JLabel matchLabel;
717  private javax.swing.JLabel numberOfChildNodesLabel;
718  private javax.swing.JTabbedPane resultViewerTabs;
719  // End of variables declaration//GEN-END:variables
720 
731  @Deprecated
732  @Override
733  public boolean isMain() {
734  return this.isMain;
735  }
736 
745  @Deprecated
746  public void setNumMatches(Integer numberOfChildNodes) {
747  this.setNumberOfChildNodes(numberOfChildNodes);
748  }
749 
757  @Deprecated
758  public void resetTabs(Node unusedSelectedNode) {
759  this.setNode(null);
760  }
761 
762 }
void setNumberOfChildNodes(Integer numberOfChildNodes)
static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, Collection< DataResultViewer > viewers)
static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView)
static void createInstanceCommon(String title, String description, Node currentRootNode, int childNodeCount, DataResultPanel resultViewPanel)
static synchronized DataContentTopComponent findInstance()
static DataResultPanel createInstanceUninitialized(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView)
void setContentViewer(DataContent customContentView)
void addResultViewer(DataResultViewer resultViewer)
static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount)

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