Autopsy  4.4.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 2011-2017 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.List;
26 import javax.swing.JTabbedPane;
27 import javax.swing.SwingUtilities;
28 import javax.swing.event.ChangeEvent;
29 import javax.swing.event.ChangeListener;
30 import org.openide.explorer.ExplorerManager;
31 import org.openide.nodes.Node;
32 import org.openide.nodes.NodeEvent;
33 import org.openide.nodes.NodeListener;
34 import org.openide.nodes.NodeMemberEvent;
35 import org.openide.nodes.NodeReorderEvent;
36 import org.openide.util.Lookup;
37 import org.openide.util.NbBundle;
43 
67 public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener {
68 
69  private static final long serialVersionUID = 1L;
70  private static final int NO_TAB_SELECTED = -1;
71  private static final String PLEASE_WAIT_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.pleasewaitNodeDisplayName");
72  private final List<DataResultViewer> resultViewers = new ArrayList<>();
73  private boolean isMain;
74  private ExplorerManager explorerManager;
76  private Node rootNode;
78  private boolean listeningToTabbedPane;
80 
93  public static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches) {
94  DataResultPanel resultPanel = new DataResultPanel(title, false);
95  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
96  resultPanel.open();
97  return resultPanel;
98  }
99 
114  public static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView) {
115  DataResultPanel resultPanel = new DataResultPanel(title, customContentView);
116  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
117  resultPanel.open();
118  return resultPanel;
119  }
120 
136  public static DataResultPanel createInstanceUninitialized(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView) {
137  DataResultPanel resultPanel = new DataResultPanel(title, customContentView);
138  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
139  return resultPanel;
140  }
141 
153  private static void createInstanceCommon(String title, String pathText, Node rootNode, int totalMatches, DataResultPanel resultViewPanel) {
154  resultViewPanel.setTitle(title);
155  resultViewPanel.setName(title);
156  resultViewPanel.setNumMatches(totalMatches);
157  resultViewPanel.setNode(rootNode);
158  resultViewPanel.setPath(pathText);
159  }
160 
166  private DataResultPanel() {
167  this.isMain = true;
168  initComponents();
169  }
170 
180  DataResultPanel(String title, boolean isMain) {
181  this();
182  this.isMain = isMain;
183  this.contentView = Lookup.getDefault().lookup(DataContent.class);
184  }
185 
195  DataResultPanel(String title, DataContent customContentView) {
196  this(title, false);
197  this.contentView = customContentView;
198  }
199 
205  @Override
206  public String getPreferredID() {
207  return getName();
208  }
209 
217  @Override
218  public boolean isMain() {
219  return this.isMain;
220  }
221 
227  @Override
228  public void setTitle(String title) {
229  setName(title);
230  }
231 
238  @Override
239  public void setPath(String pathText) {
240  this.directoryTablePath.setText(pathText);
241  }
242 
248  private void addResultViewer(DataResultViewer resultViewer) {
249  if (null != contentView) {
250  resultViewer.setContentViewer(contentView);
251  }
252  resultViewers.add(resultViewer);
253  dataResultTabbedPanel.addTab(resultViewer.getTitle(), resultViewer.getComponent());
254  }
255 
261  @Override
262  public List<DataResultViewer> getViewers() {
263  List<DataResultViewer> viewers = new ArrayList<>();
264  resultViewers.forEach((viewer) -> {
265  viewers.add(viewer);
266  });
267  return viewers;
268  }
269 
277  public void setContentViewer(DataContent customContentView) {
278  this.contentView = customContentView;
279  }
280 
285  public void open() {
286  if (null == explorerManager) {
287  /*
288  * Get an explorer manager to pass to the child result viewers. If
289  * the application components are put together as expected, this
290  * will be an explorer manager owned by a parent top component, and
291  * placed by the top component in the look up that is proxied as the
292  * action global context when the top component has focus. The
293  * sharing of this explorer manager enables the same child node
294  * selections to be made in all of the result viewers.
295  */
296  explorerManager = ExplorerManager.find(this);
297  emNodeSelectionListener = new ExplorerManagerNodeSelectionListener();
298  explorerManager.addPropertyChangeListener(emNodeSelectionListener);
299  }
300 
301  /*
302  * Load the child result viewers into the tabbed pane.
303  */
304  if (0 == dataResultTabbedPanel.getTabCount()) {
305  /*
306  * TODO (JIRA-2658): Fix the DataResultViewer extension point. When
307  * this is done, restore the implementation of DataResultViewerTable
308  * and DataREsultViewerThumbnail as DataResultViewer service
309  * providers.
310  */
311  addResultViewer(new DataResultViewerTable(this.explorerManager));
312  addResultViewer(new DataResultViewerThumbnail(this.explorerManager));
313  for (DataResultViewer factory : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
314  DataResultViewer resultViewer;
315  if (isMain) {
316  resultViewer = factory;
317  } else {
318  resultViewer = factory.createInstance();
319  }
320  addResultViewer(resultViewer);
321  }
322  }
323 
324  if (isMain && null == rootNode) {
325  setNode(rootNode);
326  }
327 
328  this.setVisible(true);
329  }
330 
339  @Override
340  public void setNode(Node rootNode) {
341  if (this.rootNode != null) {
342  this.rootNode.removeNodeListener(rootNodeListener);
343  }
344 
345  /*
346  * Deferring becoming a listener to the tabbed pane until this point
347  * eliminates handling a superfluous stateChanged event during
348  * construction.
349  */
350  if (listeningToTabbedPane == false) {
351  dataResultTabbedPanel.addChangeListener(this);
352  listeningToTabbedPane = true;
353  }
354 
355  this.rootNode = rootNode;
356  if (this.rootNode != null) {
357  rootNodeListener.reset();
358  this.rootNode.addNodeListener(rootNodeListener);
359  }
360 
361  resetTabs(this.rootNode);
362  setupTabs(this.rootNode);
363 
364  if (null != this.rootNode) {
365  int childrenCount = this.rootNode.getChildren().getNodesCount();
366  this.numberMatchLabel.setText(Integer.toString(childrenCount));
367  }
368  this.numberMatchLabel.setVisible(true);
369  }
370 
378  public Node getRootNode() {
379  return rootNode;
380  }
381 
387  public void setNumMatches(Integer numberOfChildNodes) {
388  if (this.numberMatchLabel != null) {
389  this.numberMatchLabel.setText(Integer.toString(numberOfChildNodes));
390  }
391  }
392 
399  public void setSelectedNodes(Node[] selectedNodes) {
400  this.resultViewers.forEach((viewer) -> {
401  viewer.setSelectedNodes(selectedNodes);
402  });
403  }
404 
411  private void setupTabs(Node selectedNode) {
412  /*
413  * Enable or disable the result viewer tabs based on whether or not the
414  * corresponding results viewer supports display of the selected node.
415  */
416  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) {
417  if (resultViewers.get(i).isSupported(selectedNode)) {
418  dataResultTabbedPanel.setEnabledAt(i, true);
419  } else {
420  dataResultTabbedPanel.setEnabledAt(i, false);
421  }
422  }
423 
424  /*
425  * If the selected node has a child to be selected, default the selected
426  * tab to the table result viewer. Otherwise, use the last selected tab,
427  * if it is enabled. If not, select the first enabled tab that can be
428  * found.
429  */
430  int tabToSelect = NO_TAB_SELECTED;
431  if (selectedNode instanceof TableFilterNode) {
432  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) selectedNode).getChildNodeSelectionInfo();
433  if (null != selectedChildInfo) {
434  for (int i = 0; i < resultViewers.size(); ++i) {
435  if (resultViewers.get(i) instanceof DataResultViewerTable && dataResultTabbedPanel.isEnabledAt(i)) {
436  tabToSelect = i;
437  }
438  }
439  }
440  };
441  if (NO_TAB_SELECTED == tabToSelect) {
442  tabToSelect = dataResultTabbedPanel.getSelectedIndex();
443  if ((NO_TAB_SELECTED == tabToSelect) || (!dataResultTabbedPanel.isEnabledAt(tabToSelect))) {
444  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); ++i) {
445  if (dataResultTabbedPanel.isEnabledAt(i)) {
446  tabToSelect = i;
447  break;
448  }
449  }
450  }
451  }
452 
453  /*
454  * If there is a tab to sele3ct, do so, and push the selected node to
455  * the corresponding result viewer.
456  */
457  if (NO_TAB_SELECTED != tabToSelect) {
458  dataResultTabbedPanel.setSelectedIndex(tabToSelect);
459  resultViewers.get(tabToSelect).setNode(selectedNode);
460  }
461  }
462 
469  public void resetTabs(Node unusedSelectedNode) {
470  this.resultViewers.forEach((viewer) -> {
471  viewer.resetComponent();
472  });
473  }
474 
481  @Override
482  public void stateChanged(ChangeEvent event) {
483  JTabbedPane pane = (JTabbedPane) event.getSource();
484  int currentTab = pane.getSelectedIndex();
485  if (-1 != currentTab) {
486  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
487  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
488  try {
489  currentViewer.setNode(rootNode);
490  } finally {
491  this.setCursor(null);
492  }
493  }
494  }
495 
502  public boolean canClose() {
503  /*
504  * If this is the "main" panel, only allow it to be closed when no case
505  * is open or no there are no data sources in the current case.
506  */
507  return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false;
508  }
509 
514  void close() {
515  if (null != explorerManager && null != emNodeSelectionListener) {
516  explorerManager.removePropertyChangeListener(emNodeSelectionListener);
517  explorerManager = null;
518  }
519 
520  this.resultViewers.forEach((viewer) -> {
521  viewer.setNode(null);
522  });
523 
524  if (!this.isMain) {
525  this.resultViewers.forEach((viewer) -> {
526  viewer.clearComponent();
527  });
528  this.directoryTablePath.removeAll();
529  this.directoryTablePath = null;
530  this.numberMatchLabel.removeAll();
531  this.numberMatchLabel = null;
532  this.matchLabel.removeAll();
533  this.matchLabel = null;
534  this.setLayout(null);
535  this.removeAll();
536  this.setVisible(false);
537  }
538  }
539 
543  private class ExplorerManagerNodeSelectionListener implements PropertyChangeListener {
544 
545  @Override
546  public void propertyChange(PropertyChangeEvent evt) {
547  try {
549  } catch (IllegalStateException ex) {
550  return;
551  }
552 
553  /*
554  * Only interested in node selection events.
555  */
556  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
557  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
558  try {
559  if (contentView != null) {
560  Node[] selectedNodes = explorerManager.getSelectedNodes();
561 
562  /*
563  * Pass the selected nodes to all of the result viewers
564  * sharing this explorer manager.
565  */
566  resultViewers.forEach((viewer) -> {
567  viewer.setSelectedNodes(selectedNodes);
568  });
569 
570  /*
571  * Passing null signals that either multiple nodes are
572  * selected, or no nodes are selected. This is important
573  * to the content view, since content views only work
574  * for a single node..
575  */
576  if (1 == selectedNodes.length) {
577  contentView.setNode(selectedNodes[0]);
578  } else {
579  contentView.setNode(null);
580  }
581  }
582  } finally {
583  setCursor(null);
584  }
585  }
586  }
587  }
588 
593  private class RootNodeListener implements NodeListener {
594 
595  private volatile boolean waitingForData = true;
596 
597  public void reset() {
598  waitingForData = true;
599  }
600 
601  @Override
602  public void childrenAdded(final NodeMemberEvent nme) {
603  Node[] delta = nme.getDelta();
604  updateMatches();
605 
606  /*
607  * There is a known issue in this code whereby we will only call
608  * setupTabs() once even though childrenAdded could be called
609  * multiple times. That means that each panel may not have access to
610  * all of the children when they decide if they support the content
611  */
612  if (waitingForData && containsReal(delta)) {
613  waitingForData = false;
614  if (SwingUtilities.isEventDispatchThread()) {
615  setupTabs(nme.getNode());
616  } else {
617  SwingUtilities.invokeLater(() -> {
618  setupTabs(nme.getNode());
619  });
620  }
621  }
622  }
623 
624  private boolean containsReal(Node[] delta) {
625  for (Node n : delta) {
626  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
627  return true;
628  }
629  }
630  return false;
631  }
632 
637  private void updateMatches() {
638  if (rootNode != null && rootNode.getChildren() != null) {
639  setNumMatches(rootNode.getChildren().getNodesCount());
640  }
641  }
642 
643  @Override
644  public void childrenRemoved(NodeMemberEvent nme) {
645  updateMatches();
646  }
647 
648  @Override
649  public void childrenReordered(NodeReorderEvent nre) {
650  }
651 
652  @Override
653  public void nodeDestroyed(NodeEvent ne) {
654  }
655 
656  @Override
657  public void propertyChange(PropertyChangeEvent evt) {
658  }
659  }
660 
666  @SuppressWarnings("unchecked")
667  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
668  private void initComponents() {
669 
670  directoryTablePath = new javax.swing.JLabel();
671  numberMatchLabel = new javax.swing.JLabel();
672  matchLabel = new javax.swing.JLabel();
673  dataResultTabbedPanel = new javax.swing.JTabbedPane();
674 
675  setMinimumSize(new java.awt.Dimension(0, 5));
676  setPreferredSize(new java.awt.Dimension(5, 5));
677 
678  org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N
679  directoryTablePath.setMinimumSize(new java.awt.Dimension(5, 14));
680 
681  org.openide.awt.Mnemonics.setLocalizedText(numberMatchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberMatchLabel.text")); // NOI18N
682 
683  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
684 
685  dataResultTabbedPanel.setMinimumSize(new java.awt.Dimension(0, 5));
686 
687  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
688  this.setLayout(layout);
689  layout.setHorizontalGroup(
690  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
691  .addGroup(layout.createSequentialGroup()
692  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
693  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
694  .addComponent(numberMatchLabel)
695  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
696  .addComponent(matchLabel))
697  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
698  );
699  layout.setVerticalGroup(
700  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
701  .addGroup(layout.createSequentialGroup()
702  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
703  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
704  .addComponent(numberMatchLabel)
705  .addComponent(matchLabel))
706  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
707  .addGap(0, 0, 0)
708  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
709  );
710  }// </editor-fold>//GEN-END:initComponents
711  // Variables declaration - do not modify//GEN-BEGIN:variables
712  private javax.swing.JTabbedPane dataResultTabbedPanel;
713  private javax.swing.JLabel directoryTablePath;
714  private javax.swing.JLabel matchLabel;
715  private javax.swing.JLabel numberMatchLabel;
716  // End of variables declaration//GEN-END:variables
717 
718 }
static void createInstanceCommon(String title, String pathText, Node rootNode, int totalMatches, DataResultPanel resultViewPanel)
void setContentViewer(DataContent customContentView)
ExplorerManagerNodeSelectionListener emNodeSelectionListener
void addResultViewer(DataResultViewer resultViewer)
static DataResultPanel createInstanceUninitialized(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView)
static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView)
static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches)

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.