Autopsy  4.7.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.SwingWorker;
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  rootNodeListener.reset();
374  this.currentRootNode.addNodeListener(rootNodeListener);
375  }
376 
377  this.resultViewers.forEach((viewer) -> {
378  viewer.resetComponent();
379  });
380  setupTabs(this.currentRootNode);
381 
382  if (this.currentRootNode != null) {
383  int childrenCount = this.currentRootNode.getChildren().getNodesCount();
384  this.numberOfChildNodesLabel.setText(Integer.toString(childrenCount));
385  }
386  this.numberOfChildNodesLabel.setVisible(true);
387  }
388 
396  public Node getRootNode() {
397  return currentRootNode;
398  }
399 
406  public void setNumberOfChildNodes(Integer numberOfChildNodes) {
407  this.numberOfChildNodesLabel.setText(Integer.toString(numberOfChildNodes));
408  }
409 
416  public void setSelectedNodes(Node[] selectedNodes) {
417  this.resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
418  }
419 
426  private void setupTabs(Node selectedNode) {
427  /*
428  * Enable or disable the result viewer tabs based on whether or not the
429  * corresponding results viewer supports display of the selected node.
430  */
431  for (int i = 0; i < resultViewerTabs.getTabCount(); i++) {
432  if (resultViewers.get(i).isSupported(selectedNode)) {
433  resultViewerTabs.setEnabledAt(i, true);
434  } else {
435  resultViewerTabs.setEnabledAt(i, false);
436  }
437  }
438 
439  /*
440  * If the selected node has a child to be selected, default the selected
441  * tab to the table result viewer. Otherwise, use the last selected tab,
442  * if it is enabled. If not, select the first enabled tab that can be
443  * found.
444  */
445  int tabToSelect = NO_TAB_SELECTED;
446  if (selectedNode instanceof TableFilterNode) {
447  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) selectedNode).getChildNodeSelectionInfo();
448  if (null != selectedChildInfo) {
449  for (int i = 0; i < resultViewers.size(); ++i) {
450  if (resultViewers.get(i) instanceof DataResultViewerTable && resultViewerTabs.isEnabledAt(i)) {
451  tabToSelect = i;
452  }
453  }
454  }
455  }
456  if (tabToSelect == NO_TAB_SELECTED) {
457  tabToSelect = resultViewerTabs.getSelectedIndex();
458  if ((tabToSelect == NO_TAB_SELECTED) || (!resultViewerTabs.isEnabledAt(tabToSelect))) {
459  for (int i = 0; i < resultViewerTabs.getTabCount(); ++i) {
460  if (resultViewerTabs.isEnabledAt(i)) {
461  tabToSelect = i;
462  break;
463  }
464  }
465  }
466  }
467 
468  /*
469  * If there is a tab to select, do so, and push the selected node to the
470  * corresponding result viewer.
471  */
472  if (tabToSelect != NO_TAB_SELECTED) {
473  resultViewerTabs.setSelectedIndex(tabToSelect);
474  resultViewers.get(tabToSelect).setNode(selectedNode);
475  }
476  }
477 
484  @Override
485  public void stateChanged(ChangeEvent event) {
486  JTabbedPane pane = (JTabbedPane) event.getSource();
487  int currentTab = pane.getSelectedIndex();
488  if (currentTab != DataResultPanel.NO_TAB_SELECTED) {
489  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
490  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
491  try {
492  currentViewer.setNode(currentRootNode);
493  } finally {
494  this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
495  }
496  }
497  }
498 
505  public boolean canClose() {
506  /*
507  * If this is the "main" panel, only allow it to be closed when no case
508  * is open or no there are no data sources in the current case.
509  */
510  Case openCase;
511  try {
512  openCase = Case.getCurrentCaseThrows();
513  } catch (NoCurrentCaseException ex) {
514  return true;
515  }
516  return (!this.isMain) || openCase.hasData() == false;
517  }
518 
524  void close() {
525  if (explorerManager != null && explorerManagerListener != null) {
526  explorerManager.removePropertyChangeListener(explorerManagerListener);
527  explorerManager = null;
528  }
529 
530  this.resultViewers.forEach((viewer) -> viewer.setNode(null));
531 
532  if (!this.isMain) { // RJCTODO: What?
533  this.resultViewers.forEach(DataResultViewer::clearComponent);
534  this.descriptionLabel.removeAll();
535  this.numberOfChildNodesLabel.removeAll();
536  this.matchLabel.removeAll();
537  this.setLayout(null);
538  this.removeAll();
539  this.setVisible(false);
540  }
541  }
542 
543  @Override
544  public ExplorerManager getExplorerManager() {
545  return explorerManager;
546 
547  }
548 
559  private class ExplorerManagerListener implements PropertyChangeListener {
560 
561  @Override
562  public void propertyChange(PropertyChangeEvent evt) {
563  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES) && contentView != null) {
564  /*
565  * Pass a single node selection in a result viewer to the
566  * content view. Note that passing null to the content view
567  * signals that either multiple nodes are selected, or a
568  * previous selection has been cleared. This is important to the
569  * content view, since its child content viewers only work for a
570  * single node.
571  */
572  Node[] selectedNodes = explorerManager.getSelectedNodes();
573  if (selectedNodes.length == 1) {
574  contentView.setNode(selectedNodes[0]);
575  } else {
576  contentView.setNode(null);
577  }
578  }
579  }
580  }
581 
585  class SetupTabsChildrenWorker extends SwingWorker<Void, Void> {
586 
587  private final Node childNode;
588 
589  SetupTabsChildrenWorker(Node aChildNode) {
590  childNode = aChildNode;
591  }
592 
593  @Override
594  protected Void doInBackground() throws Exception {
595  setupTabs(childNode);
596  return null;
597  }
598 
599  @Override
600  protected void done() {
601  setupTabs(childNode);
602  }
603  }
604 
609  // RJCTODO: Why do we need this?
610  private class RootNodeListener implements NodeListener {
611 
612  private volatile boolean waitingForData = true;
613 
614  public void reset() {
615  waitingForData = true;
616  }
617 
618  @Override
619  public void childrenAdded(final NodeMemberEvent nme) {
620  Node[] delta = nme.getDelta();
621  updateMatches();
622 
623  /*
624  * There is a known issue in this code whereby we will only call
625  * setupTabs() once even though childrenAdded could be called
626  * multiple times. That means that each panel may not have access to
627  * all of the children when they decide if they support the content
628  */
629  if (waitingForData && containsReal(delta)) {
630  waitingForData = false;
631  Node childNode = nme.getNode();
632  new SetupTabsChildrenWorker(childNode).execute();
633  }
634  }
635 
636  private boolean containsReal(Node[] delta) {
637  for (Node n : delta) {
638  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
639  return true;
640  }
641  }
642  return false;
643  }
644 
649  private void updateMatches() {
650  if (currentRootNode != null && currentRootNode.getChildren() != null) {
651  setNumMatches(currentRootNode.getChildren().getNodesCount());
652  }
653  }
654 
655  @Override
656  public void childrenRemoved(NodeMemberEvent nme) {
657  updateMatches();
658  }
659 
660  @Override
661  public void childrenReordered(NodeReorderEvent nre) {
662  }
663 
664  @Override
665  public void nodeDestroyed(NodeEvent ne) {
666  }
667 
668  @Override
669  public void propertyChange(PropertyChangeEvent evt) {
670  }
671  }
672 
678  @SuppressWarnings("unchecked")
679  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
680  private void initComponents() {
681 
682  descriptionLabel = new javax.swing.JLabel();
683  numberOfChildNodesLabel = new javax.swing.JLabel();
684  matchLabel = new javax.swing.JLabel();
685  resultViewerTabs = new javax.swing.JTabbedPane();
686 
687  setMinimumSize(new java.awt.Dimension(0, 5));
688  setPreferredSize(new java.awt.Dimension(5, 5));
689 
690  org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.descriptionLabel.text")); // NOI18N
691  descriptionLabel.setMinimumSize(new java.awt.Dimension(5, 14));
692 
693  org.openide.awt.Mnemonics.setLocalizedText(numberOfChildNodesLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberOfChildNodesLabel.text")); // NOI18N
694 
695  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
696 
697  resultViewerTabs.setMinimumSize(new java.awt.Dimension(0, 5));
698 
699  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
700  this.setLayout(layout);
701  layout.setHorizontalGroup(
702  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
703  .addGroup(layout.createSequentialGroup()
704  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
705  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
706  .addComponent(numberOfChildNodesLabel)
707  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
708  .addComponent(matchLabel))
709  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
710  );
711  layout.setVerticalGroup(
712  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
713  .addGroup(layout.createSequentialGroup()
714  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
715  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
716  .addComponent(numberOfChildNodesLabel)
717  .addComponent(matchLabel))
718  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
719  .addGap(0, 0, 0)
720  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
721  );
722  }// </editor-fold>//GEN-END:initComponents
723  // Variables declaration - do not modify//GEN-BEGIN:variables
724  private javax.swing.JLabel descriptionLabel;
725  private javax.swing.JLabel matchLabel;
726  private javax.swing.JLabel numberOfChildNodesLabel;
727  private javax.swing.JTabbedPane resultViewerTabs;
728  // End of variables declaration//GEN-END:variables
729 
740  @Deprecated
741  @Override
742  public boolean isMain() {
743  return this.isMain;
744  }
745 
754  @Deprecated
755  public void setNumMatches(Integer numberOfChildNodes) {
756  this.setNumberOfChildNodes(numberOfChildNodes);
757  }
758 
766  @Deprecated
767  public void resetTabs(Node unusedSelectedNode) {
768  this.setNode(null);
769  }
770 
771 }
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-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.