Autopsy  4.6.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 
74 public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener, ExplorerManager.Provider {
75 
76  private static final long serialVersionUID = 1L;
77  private static final int NO_TAB_SELECTED = -1;
78  private static final String PLEASE_WAIT_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.pleasewaitNodeDisplayName");
79  private final boolean isMain;
80  private final List<DataResultViewer> resultViewers;
84  private ExplorerManager explorerManager;
85  private Node currentRootNode;
86  private boolean listeningToTabbedPane;
87 
105  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount) {
106  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), null);
107  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
108  resultPanel.open();
109  return resultPanel;
110  }
111 
132  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, Collection<DataResultViewer> viewers) {
133  DataResultPanel resultPanel = new DataResultPanel(title, false, viewers, null);
134  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
135  resultPanel.open();
136  return resultPanel;
137  }
138 
159  public static DataResultPanel createInstance(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView) {
160  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), customContentView);
161  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
162  resultPanel.open();
163  return resultPanel;
164  }
165 
185  public static DataResultPanel createInstanceUninitialized(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView) {
186  DataResultPanel resultPanel = new DataResultPanel(title, false, Collections.emptyList(), customContentView);
187  createInstanceCommon(title, description, currentRootNode, childNodeCount, resultPanel);
188  return resultPanel;
189  }
190 
202  private static void createInstanceCommon(String title, String description, Node currentRootNode, int childNodeCount, DataResultPanel resultViewPanel) {
203  resultViewPanel.setTitle(title);
204  resultViewPanel.setName(title);
205  resultViewPanel.setNumberOfChildNodes(childNodeCount);
206  resultViewPanel.setNode(currentRootNode);
207  resultViewPanel.setPath(description);
208  }
209 
229  DataResultPanel(String title, boolean isMain, Collection<DataResultViewer> viewers, DataContent customContentView) {
230  this.setTitle(title);
231  this.isMain = isMain;
232  if (customContentView == null) {
233  this.contentView = Lookup.getDefault().lookup(DataContent.class);
234  } else {
235  this.contentView = customContentView;
236  }
237  this.resultViewers = new ArrayList<>(viewers);
238  this.explorerManagerListener = new ExplorerManagerListener();
239  this.rootNodeListener = new RootNodeListener();
240  initComponents();
241  }
242 
249  @Override
250  public String getPreferredID() {
251  return getName();
252  }
253 
259  @Override
260  public void setTitle(String title) {
261  setName(title);
262  }
263 
270  @Override
271  public void setPath(String description) {
272  this.descriptionLabel.setText(description);
273  }
274 
280  public void addResultViewer(DataResultViewer resultViewer) {
281  resultViewers.add(resultViewer);
282  resultViewerTabs.addTab(resultViewer.getTitle(), resultViewer.getComponent());
283  }
284 
290  @Override
291  public List<DataResultViewer> getViewers() {
292  return Collections.unmodifiableList(resultViewers);
293  }
294 
302  public void setContentViewer(DataContent customContentView) {
303  this.contentView = customContentView;
304  }
305 
310  public void open() {
311  /*
312  * The parent top component is expected to be an explorer manager
313  * provider that exposes a lookup maintained by its explorer manager to
314  * the actions global context. The child result view panel will then
315  * find the parent top component's explorer manager at runtime, so that
316  * it can act as an explorer manager provider for its child result
317  * viewers. This connects the nodes displayed in the result viewers to
318  * the actions global context.
319  */
320  if (this.explorerManager == null) {
321  this.explorerManager = ExplorerManager.find(this);
322  this.explorerManager.addPropertyChangeListener(this.explorerManagerListener);
323  }
324 
325  /*
326  * Load either the supplied result viewers or the result viewers
327  * provided by the result viewer extension point into the tabbed pane.
328  * If loading from the extension point and distinct result viewer
329  * instances MUST be created if this is not the "main" result view.
330  */
331  if (this.resultViewerTabs.getTabCount() == 0) {
332  if (this.resultViewers.isEmpty()) {
333  for (DataResultViewer resultViewer : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
334  if (this.isMain) {
335  this.resultViewers.add(resultViewer);
336  } else {
337  this.resultViewers.add(resultViewer.createInstance());
338  }
339  }
340  }
341  this.resultViewers.forEach((resultViewer) -> resultViewerTabs.addTab(resultViewer.getTitle(), resultViewer.getComponent()));
342  }
343 
344  this.setVisible(true);
345  }
346 
357  @Override
358  public void setNode(Node rootNode) {
359  if (this.currentRootNode != null) {
360  this.currentRootNode.removeNodeListener(rootNodeListener);
361  }
362 
363  /*
364  * Deferring becoming a listener to the tabbed pane until this point
365  * eliminates handling a superfluous stateChanged event during
366  * construction.
367  */
368  if (listeningToTabbedPane == false) {
369  resultViewerTabs.addChangeListener(this);
370  listeningToTabbedPane = true;
371  }
372 
373  this.currentRootNode = rootNode;
374  if (this.currentRootNode != null) {
375  rootNodeListener.reset();
376  this.currentRootNode.addNodeListener(rootNodeListener);
377  }
378 
379  this.resultViewers.forEach((viewer) -> {
380  viewer.resetComponent();
381  });
382  setupTabs(this.currentRootNode);
383 
384  if (this.currentRootNode != null) {
385  int childrenCount = this.currentRootNode.getChildren().getNodesCount();
386  this.numberOfChildNodesLabel.setText(Integer.toString(childrenCount));
387  }
388  this.numberOfChildNodesLabel.setVisible(true);
389  }
390 
398  public Node getRootNode() {
399  return currentRootNode;
400  }
401 
408  public void setNumberOfChildNodes(Integer numberOfChildNodes) {
409  this.numberOfChildNodesLabel.setText(Integer.toString(numberOfChildNodes));
410  }
411 
418  public void setSelectedNodes(Node[] selectedNodes) {
419  this.resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
420  }
421 
428  private void setupTabs(Node selectedNode) {
429  /*
430  * Enable or disable the result viewer tabs based on whether or not the
431  * corresponding results viewer supports display of the selected node.
432  */
433  for (int i = 0; i < resultViewerTabs.getTabCount(); i++) {
434  if (resultViewers.get(i).isSupported(selectedNode)) {
435  resultViewerTabs.setEnabledAt(i, true);
436  } else {
437  resultViewerTabs.setEnabledAt(i, false);
438  }
439  }
440 
441  /*
442  * If the selected node has a child to be selected, default the selected
443  * tab to the table result viewer. Otherwise, use the last selected tab,
444  * if it is enabled. If not, select the first enabled tab that can be
445  * found.
446  */
447  int tabToSelect = NO_TAB_SELECTED;
448  if (selectedNode instanceof TableFilterNode) {
449  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) selectedNode).getChildNodeSelectionInfo();
450  if (null != selectedChildInfo) {
451  for (int i = 0; i < resultViewers.size(); ++i) {
452  if (resultViewers.get(i) instanceof DataResultViewerTable && resultViewerTabs.isEnabledAt(i)) {
453  tabToSelect = i;
454  }
455  }
456  }
457  };
458  if (tabToSelect == NO_TAB_SELECTED) {
459  tabToSelect = resultViewerTabs.getSelectedIndex();
460  if ((tabToSelect == NO_TAB_SELECTED) || (!resultViewerTabs.isEnabledAt(tabToSelect))) {
461  for (int i = 0; i < resultViewerTabs.getTabCount(); ++i) {
462  if (resultViewerTabs.isEnabledAt(i)) {
463  tabToSelect = i;
464  break;
465  }
466  }
467  }
468  }
469 
470  /*
471  * If there is a tab to select, do so, and push the selected node to the
472  * corresponding result viewer.
473  */
474  if (tabToSelect != NO_TAB_SELECTED) {
475  resultViewerTabs.setSelectedIndex(tabToSelect);
476  resultViewers.get(tabToSelect).setNode(selectedNode);
477  }
478  }
479 
486  @Override
487  public void stateChanged(ChangeEvent event) {
488  JTabbedPane pane = (JTabbedPane) event.getSource();
489  int currentTab = pane.getSelectedIndex();
490  if (currentTab != DataResultPanel.NO_TAB_SELECTED) {
491  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
492  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
493  try {
494  currentViewer.setNode(currentRootNode);
495  } finally {
496  this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
497  }
498  }
499  }
500 
505  void close() {
506  if (explorerManager != null && explorerManagerListener != null) {
507  explorerManager.removePropertyChangeListener(explorerManagerListener);
508  explorerManager = null;
509  }
510 
511  this.resultViewers.forEach((viewer) -> viewer.setNode(null));
512 
513  if (!this.isMain) { // RJCTODO: What?
514  this.resultViewers.forEach(DataResultViewer::clearComponent);
515  this.descriptionLabel.removeAll();
516  this.numberOfChildNodesLabel.removeAll();
517  this.matchLabel.removeAll();
518  this.setLayout(null);
519  this.removeAll();
520  this.setVisible(false);
521  }
522  }
523 
524  @Override
525  public ExplorerManager getExplorerManager() {
526  return explorerManager;
527 
528  }
529 
540  private class ExplorerManagerListener implements PropertyChangeListener {
541 
542  @Override
543  public void propertyChange(PropertyChangeEvent evt) {
544  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES) && contentView != null) {
545  /*
546  * Pass a single node selection in a result viewer to the
547  * content view. Note that passing null to the content view
548  * signals that either multiple nodes are selected, or a
549  * previous selection has been cleared. This is important to the
550  * content view, since its child content viewers only work for a
551  * single node.
552  */
553  Node[] selectedNodes = explorerManager.getSelectedNodes();
554  if (1 == selectedNodes.length) {
555  contentView.setNode(selectedNodes[0]);
556  } else {
557  contentView.setNode(null);
558  }
559  }
560  }
561  }
562 
566  class SetupTabsChildrenWorker extends SwingWorker<Void, Void> {
567 
568  private final Node childNode;
569  SetupTabsChildrenWorker(Node aChildNode) {
570  childNode = aChildNode;
571  }
572  @Override
573  protected Void doInBackground() throws Exception {
574  setupTabs(childNode);
575  return null;
576  }
577 
578  @Override
579  protected void done() {
580  setupTabs(childNode);
581  }
582  }
587  // RJCTODO: Why do we need this?
588  private class RootNodeListener implements NodeListener {
589 
590  private volatile boolean waitingForData = true;
591 
592  public void reset() {
593  waitingForData = true;
594  }
595 
596  @Override
597  public void childrenAdded(final NodeMemberEvent nme) {
598  Node[] delta = nme.getDelta();
599  updateMatches();
600 
601  /*
602  * There is a known issue in this code whereby we will only call
603  * setupTabs() once even though childrenAdded could be called
604  * multiple times. That means that each panel may not have access to
605  * all of the children when they decide if they support the content
606  */
607  if (waitingForData && containsReal(delta)) {
608  waitingForData = false;
609  Node childNode = nme.getNode();
610  new SetupTabsChildrenWorker(childNode).execute();
611  }
612  }
613 
614  private boolean containsReal(Node[] delta) {
615  for (Node n : delta) {
616  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
617  return true;
618  }
619  }
620  return false;
621  }
622 
627  private void updateMatches() {
628  if (currentRootNode != null && currentRootNode.getChildren() != null) {
629  setNumMatches(currentRootNode.getChildren().getNodesCount());
630  }
631  }
632 
633  @Override
634  public void childrenRemoved(NodeMemberEvent nme) {
635  updateMatches();
636  }
637 
638  @Override
639  public void childrenReordered(NodeReorderEvent nre) {
640  }
641 
642  @Override
643  public void nodeDestroyed(NodeEvent ne) {
644  }
645 
646  @Override
647  public void propertyChange(PropertyChangeEvent evt) {
648  }
649  }
650 
656  @SuppressWarnings("unchecked")
657  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
658  private void initComponents() {
659 
660  descriptionLabel = new javax.swing.JLabel();
661  numberOfChildNodesLabel = new javax.swing.JLabel();
662  matchLabel = new javax.swing.JLabel();
663  resultViewerTabs = new javax.swing.JTabbedPane();
664 
665  setMinimumSize(new java.awt.Dimension(0, 5));
666  setPreferredSize(new java.awt.Dimension(5, 5));
667 
668  org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.descriptionLabel.text")); // NOI18N
669  descriptionLabel.setMinimumSize(new java.awt.Dimension(5, 14));
670 
671  org.openide.awt.Mnemonics.setLocalizedText(numberOfChildNodesLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberOfChildNodesLabel.text")); // NOI18N
672 
673  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
674 
675  resultViewerTabs.setMinimumSize(new java.awt.Dimension(0, 5));
676 
677  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
678  this.setLayout(layout);
679  layout.setHorizontalGroup(
680  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
681  .addGroup(layout.createSequentialGroup()
682  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
683  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
684  .addComponent(numberOfChildNodesLabel)
685  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
686  .addComponent(matchLabel))
687  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
688  );
689  layout.setVerticalGroup(
690  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
691  .addGroup(layout.createSequentialGroup()
692  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
693  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
694  .addComponent(numberOfChildNodesLabel)
695  .addComponent(matchLabel))
696  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
697  .addGap(0, 0, 0)
698  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
699  );
700  }// </editor-fold>//GEN-END:initComponents
701  // Variables declaration - do not modify//GEN-BEGIN:variables
702  private javax.swing.JLabel descriptionLabel;
703  private javax.swing.JLabel matchLabel;
704  private javax.swing.JLabel numberOfChildNodesLabel;
705  private javax.swing.JTabbedPane resultViewerTabs;
706  // End of variables declaration//GEN-END:variables
707 
718  @Deprecated
719  @Override
720  public boolean isMain() {
721  return this.isMain;
722  }
723 
732  @Deprecated
733  public void setNumMatches(Integer numberOfChildNodes) {
734  this.setNumberOfChildNodes(numberOfChildNodes);
735  }
736 
744  @Deprecated
745  public void resetTabs(Node unusedSelectedNode) {
746  this.setNode(null);
747  }
748 
749 }
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 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 May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.