Autopsy  4.9.1
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  tabToSelect = resultViewerTabs.getSelectedIndex();
465  if ((tabToSelect == NO_TAB_SELECTED) || (!resultViewerTabs.isEnabledAt(tabToSelect))) {
466  for (int i = 0; i < resultViewerTabs.getTabCount(); ++i) {
467  if (resultViewerTabs.isEnabledAt(i)) {
468  tabToSelect = i;
469  break;
470  }
471  }
472  }
473  }
474 
475  /*
476  * If there is a tab to select, do so, and push the selected node to the
477  * corresponding result viewer.
478  */
479  if (tabToSelect != NO_TAB_SELECTED) {
480  resultViewerTabs.setSelectedIndex(tabToSelect);
481  resultViewers.get(tabToSelect).setNode(selectedNode);
482  }
483  }
484 
491  @Override
492  public void stateChanged(ChangeEvent event) {
493  JTabbedPane pane = (JTabbedPane) event.getSource();
494  int currentTab = pane.getSelectedIndex();
495  if (currentTab != DataResultPanel.NO_TAB_SELECTED) {
496  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
497  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
498  try {
499  currentViewer.setNode(currentRootNode);
500  } finally {
501  this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
502  }
503  }
504  }
505 
512  public boolean canClose() {
513  /*
514  * If this is the "main" panel, only allow it to be closed when no case
515  * is open or no there are no data sources in the current case.
516  */
517  Case openCase;
518  try {
519  openCase = Case.getCurrentCaseThrows();
520  } catch (NoCurrentCaseException ex) {
521  return true;
522  }
523  return (!this.isMain) || openCase.hasData() == false;
524  }
525 
530  void close() {
531  if (explorerManager != null && explorerManagerListener != null) {
532  explorerManager.removePropertyChangeListener(explorerManagerListener);
533  explorerManager = null;
534  }
535 
536  this.resultViewers.forEach((viewer) -> viewer.setNode(null));
537 
538  if (!this.isMain) {
539  this.resultViewers.forEach(DataResultViewer::clearComponent);
540  this.descriptionLabel.removeAll();
541  this.numberOfChildNodesLabel.removeAll();
542  this.matchLabel.removeAll();
543  this.setLayout(null);
544  this.removeAll();
545  this.setVisible(false);
546  }
547  }
548 
549  @Override
550  public ExplorerManager getExplorerManager() {
551  return explorerManager;
552 
553  }
554 
565  private class ExplorerManagerListener implements PropertyChangeListener {
566 
567  @Override
568  public void propertyChange(PropertyChangeEvent evt) {
569  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES) && contentView != null) {
570  /*
571  * Pass a single node selection in a result viewer to the
572  * content view. Note that passing null to the content view
573  * signals that either multiple nodes are selected, or a
574  * previous selection has been cleared. This is important to the
575  * content view, since its child content viewers only work for a
576  * single node.
577  */
578  Node[] selectedNodes = explorerManager.getSelectedNodes();
579  if (selectedNodes.length == 1) {
580  contentView.setNode(selectedNodes[0]);
581  } else {
582  contentView.setNode(null);
583  }
584  }
585  }
586  }
587 
594  private class RootNodeListener implements NodeListener {
595 
596  //it is assumed we are still waiting for data when the node is initially constructed
597  private volatile boolean waitingForData = true;
598 
599  public void reset() {
600  waitingForData = true;
601  }
602 
603  @Override
604  public void childrenAdded(final NodeMemberEvent nme) {
605  Node[] delta = nme.getDelta();
606  updateMatches();
607 
608  /*
609  * Ensures that after the initial call to setupTabs in the
610  * DataResultPanel.setNode method that we only call setupTabs one
611  * additional time. This is to account for the transition that is
612  * possible from a "Please wait..." node or a tab with no results in
613  * it and a tab containing data and thereby having all of it's
614  * columns.
615  */
616  if (waitingForData && containsReal(delta)) {
617  waitingForData = false;
618  if (SwingUtilities.isEventDispatchThread()) {
619  setupTabs(nme.getNode());
620  } else {
621  SwingUtilities.invokeLater(() -> {
622  setupTabs(nme.getNode());
623  });
624  }
625  }
626  }
627 
628  private boolean containsReal(Node[] delta) {
629  for (Node n : delta) {
630  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
631  return true;
632  }
633  }
634  return false;
635  }
636 
641  private void updateMatches() {
642  if (currentRootNode != null && currentRootNode.getChildren() != null) {
643  setNumMatches(currentRootNode.getChildren().getNodesCount());
644  }
645  }
646 
647  @Override
648  public void childrenRemoved(NodeMemberEvent nme) {
649  updateMatches();
650  }
651 
652  @Override
653  public void childrenReordered(NodeReorderEvent nre) {
654  }
655 
656  @Override
657  public void nodeDestroyed(NodeEvent ne) {
658  }
659 
660  @Override
661  public void propertyChange(PropertyChangeEvent evt) {
662  }
663  }
664 
670  @SuppressWarnings("unchecked")
671  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
672  private void initComponents() {
673 
674  descriptionLabel = new javax.swing.JLabel();
675  numberOfChildNodesLabel = new javax.swing.JLabel();
676  matchLabel = new javax.swing.JLabel();
677  resultViewerTabs = new javax.swing.JTabbedPane();
678 
679  setMinimumSize(new java.awt.Dimension(0, 5));
680  setPreferredSize(new java.awt.Dimension(5, 5));
681 
682  org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.descriptionLabel.text")); // NOI18N
683  descriptionLabel.setMinimumSize(new java.awt.Dimension(5, 14));
684 
685  org.openide.awt.Mnemonics.setLocalizedText(numberOfChildNodesLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberOfChildNodesLabel.text")); // NOI18N
686 
687  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
688 
689  resultViewerTabs.setMinimumSize(new java.awt.Dimension(0, 5));
690 
691  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
692  this.setLayout(layout);
693  layout.setHorizontalGroup(
694  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
695  .addGroup(layout.createSequentialGroup()
696  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
697  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
698  .addComponent(numberOfChildNodesLabel)
699  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
700  .addComponent(matchLabel))
701  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
702  );
703  layout.setVerticalGroup(
704  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
705  .addGroup(layout.createSequentialGroup()
706  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
707  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
708  .addComponent(numberOfChildNodesLabel)
709  .addComponent(matchLabel))
710  .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
711  .addGap(0, 0, 0)
712  .addComponent(resultViewerTabs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
713  );
714  }// </editor-fold>//GEN-END:initComponents
715  // Variables declaration - do not modify//GEN-BEGIN:variables
716  private javax.swing.JLabel descriptionLabel;
717  private javax.swing.JLabel matchLabel;
718  private javax.swing.JLabel numberOfChildNodesLabel;
719  private javax.swing.JTabbedPane resultViewerTabs;
720  // End of variables declaration//GEN-END:variables
721 
732  @Deprecated
733  @Override
734  public boolean isMain() {
735  return this.isMain;
736  }
737 
746  @Deprecated
747  public void setNumMatches(Integer numberOfChildNodes) {
748  this.setNumberOfChildNodes(numberOfChildNodes);
749  }
750 
758  @Deprecated
759  public void resetTabs(Node unusedSelectedNode) {
760  this.setNode(null);
761  }
762 
763 }
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-2018 Basis Technology. Generated on: Tue Dec 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.