Autopsy  4.5.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 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.Collections;
26 import java.util.List;
27 import javax.swing.JTabbedPane;
28 import javax.swing.SwingUtilities;
29 import javax.swing.event.ChangeEvent;
30 import javax.swing.event.ChangeListener;
31 import org.openide.explorer.ExplorerManager;
32 import org.openide.nodes.Node;
33 import org.openide.nodes.NodeEvent;
34 import org.openide.nodes.NodeListener;
35 import org.openide.nodes.NodeMemberEvent;
36 import org.openide.nodes.NodeReorderEvent;
37 import org.openide.util.Lookup;
38 import org.openide.util.NbBundle;
44 
68 public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener, ExplorerManager.Provider {
69 
70  private static final long serialVersionUID = 1L;
71  private static final int NO_TAB_SELECTED = -1;
72  private static final String PLEASE_WAIT_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.pleasewaitNodeDisplayName");
73  private final List<DataResultViewer> resultViewers = new ArrayList<>();
74  private boolean isMain;
75  private ExplorerManager explorerManager;
77  private Node rootNode;
79  private boolean listeningToTabbedPane;
81 
94  public static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches) {
95  DataResultPanel resultPanel = new DataResultPanel(title, false);
96  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
97  resultPanel.open();
98  return resultPanel;
99  }
100 
115  public static DataResultPanel createInstance(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView) {
116  DataResultPanel resultPanel = new DataResultPanel(title, customContentView);
117  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
118  resultPanel.open();
119  return resultPanel;
120  }
121 
137  public static DataResultPanel createInstanceUninitialized(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView) {
138  DataResultPanel resultPanel = new DataResultPanel(title, customContentView);
139  createInstanceCommon(title, pathText, rootNode, totalMatches, resultPanel);
140  return resultPanel;
141  }
142 
154  private static void createInstanceCommon(String title, String pathText, Node rootNode, int totalMatches, DataResultPanel resultViewPanel) {
155  resultViewPanel.setTitle(title);
156  resultViewPanel.setName(title);
157  resultViewPanel.setNumMatches(totalMatches);
158  resultViewPanel.setNode(rootNode);
159  resultViewPanel.setPath(pathText);
160  }
161 
169  DataResultPanel(String title, boolean isMain) {
170  this(isMain, Lookup.getDefault().lookup(DataContent.class));
171  setTitle(title);
172  }
173 
174  private DataResultPanel(boolean isMain, DataContent contentView) {
175  this.isMain = isMain;
176  this.contentView = contentView;
177  initComponents();
178  }
179 
187  DataResultPanel(String title, DataContent customContentView) {
188  this(false, customContentView);
189  }
190 
196  @Override
197  public String getPreferredID() {
198  return getName();
199  }
200 
208  @Override
209  public boolean isMain() {
210  return this.isMain;
211  }
212 
218  @Override
219  public void setTitle(String title) {
220  setName(title);
221  }
222 
229  @Override
230  public void setPath(String pathText) {
231  this.directoryTablePath.setText(pathText);
232  }
233 
239  public void addResultViewer(DataResultViewer resultViewer) {
240  resultViewers.add(resultViewer);
241  dataResultTabbedPanel.addTab(resultViewer.getTitle(), resultViewer.getComponent());
242  }
243 
249  @Override
250  public List<DataResultViewer> getViewers() {
251  return Collections.unmodifiableList(resultViewers);
252  }
253 
261  public void setContentViewer(DataContent customContentView) {
262  this.contentView = customContentView;
263  }
264 
269  public void open() {
270  if (null == explorerManager) {
271  /*
272  * Get an explorer manager to pass to the child result viewers. If
273  * the application components are put together as expected, this
274  * will be an explorer manager owned by a parent top component, and
275  * placed by the top component in the look up that is proxied as the
276  * action global context when the top component has focus. The
277  * sharing of this explorer manager enables the same child node
278  * selections to be made in all of the result viewers.
279  */
280  explorerManager = ExplorerManager.find(this);
281  emNodeSelectionListener = new ExplorerManagerNodeSelectionListener();
282  explorerManager.addPropertyChangeListener(emNodeSelectionListener);
283  }
284 
285  /*
286  * Load the child result viewers into the tabbed pane.
287  */
288  if (0 == dataResultTabbedPanel.getTabCount()) {
289  /*
290  * TODO (JIRA-2658): Fix the DataResultViewer extension point. When
291  * this is done, restore the implementation of DataResultViewerTable
292  * and DataREsultViewerThumbnail as DataResultViewer service
293  * providers.
294  */
295  addResultViewer(new DataResultViewerTable(this.explorerManager));
296  addResultViewer(new DataResultViewerThumbnail(this.explorerManager));
297  for (DataResultViewer factory : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
298  DataResultViewer resultViewer;
299  if (isMain) {
300  resultViewer = factory;
301  } else {
302  resultViewer = factory.createInstance();
303  }
304  addResultViewer(resultViewer);
305  }
306  }
307 
308  if (isMain && null == rootNode) {
309  setNode(rootNode);
310  }
311 
312  this.setVisible(true);
313  }
314 
323  @Override
324  public void setNode(Node rootNode) {
325  if (this.rootNode != null) {
326  this.rootNode.removeNodeListener(rootNodeListener);
327  }
328 
329  /*
330  * Deferring becoming a listener to the tabbed pane until this point
331  * eliminates handling a superfluous stateChanged event during
332  * construction.
333  */
334  if (listeningToTabbedPane == false) {
335  dataResultTabbedPanel.addChangeListener(this);
336  listeningToTabbedPane = true;
337  }
338 
339  this.rootNode = rootNode;
340  if (this.rootNode != null) {
341  rootNodeListener.reset();
342  this.rootNode.addNodeListener(rootNodeListener);
343  }
344 
345  resetTabs(this.rootNode);
346  setupTabs(this.rootNode);
347 
348  if (null != this.rootNode) {
349  int childrenCount = this.rootNode.getChildren().getNodesCount();
350  this.numberMatchLabel.setText(Integer.toString(childrenCount));
351  }
352  this.numberMatchLabel.setVisible(true);
353  }
354 
362  public Node getRootNode() {
363  return rootNode;
364  }
365 
371  public void setNumMatches(Integer numberOfChildNodes) {
372  if (this.numberMatchLabel != null) {
373  this.numberMatchLabel.setText(Integer.toString(numberOfChildNodes));
374  }
375  }
376 
383  public void setSelectedNodes(Node[] selectedNodes) {
384  this.resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
385  }
386 
393  private void setupTabs(Node selectedNode) {
394  /*
395  * Enable or disable the result viewer tabs based on whether or not the
396  * corresponding results viewer supports display of the selected node.
397  */
398  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) {
399  if (resultViewers.get(i).isSupported(selectedNode)) {
400  dataResultTabbedPanel.setEnabledAt(i, true);
401  } else {
402  dataResultTabbedPanel.setEnabledAt(i, false);
403  }
404  }
405 
406  /*
407  * If the selected node has a child to be selected, default the selected
408  * tab to the table result viewer. Otherwise, use the last selected tab,
409  * if it is enabled. If not, select the first enabled tab that can be
410  * found.
411  */
412  int tabToSelect = NO_TAB_SELECTED;
413  if (selectedNode instanceof TableFilterNode) {
414  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) selectedNode).getChildNodeSelectionInfo();
415  if (null != selectedChildInfo) {
416  for (int i = 0; i < resultViewers.size(); ++i) {
417  if (resultViewers.get(i) instanceof DataResultViewerTable && dataResultTabbedPanel.isEnabledAt(i)) {
418  tabToSelect = i;
419  }
420  }
421  }
422  };
423  if (NO_TAB_SELECTED == tabToSelect) {
424  tabToSelect = dataResultTabbedPanel.getSelectedIndex();
425  if ((NO_TAB_SELECTED == tabToSelect) || (!dataResultTabbedPanel.isEnabledAt(tabToSelect))) {
426  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); ++i) {
427  if (dataResultTabbedPanel.isEnabledAt(i)) {
428  tabToSelect = i;
429  break;
430  }
431  }
432  }
433  }
434 
435  /*
436  * If there is a tab to sele3ct, do so, and push the selected node to
437  * the corresponding result viewer.
438  */
439  if (NO_TAB_SELECTED != tabToSelect) {
440  dataResultTabbedPanel.setSelectedIndex(tabToSelect);
441  resultViewers.get(tabToSelect).setNode(selectedNode);
442  }
443  }
444 
451  public void resetTabs(Node unusedSelectedNode) {
452  this.resultViewers.forEach((viewer) -> {
453  viewer.resetComponent();
454  });
455  }
456 
463  @Override
464  public void stateChanged(ChangeEvent event) {
465  JTabbedPane pane = (JTabbedPane) event.getSource();
466  int currentTab = pane.getSelectedIndex();
467  if (-1 != currentTab) {
468  DataResultViewer currentViewer = this.resultViewers.get(currentTab);
469  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
470  try {
471  currentViewer.setNode(rootNode);
472  } finally {
473  this.setCursor(null);
474  }
475  }
476  }
477 
484  public boolean canClose() {
485  /*
486  * If this is the "main" panel, only allow it to be closed when no case
487  * is open or no there are no data sources in the current case.
488  */
489  return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false;
490  }
491 
496  void close() {
497  if (null != explorerManager && null != emNodeSelectionListener) {
498  explorerManager.removePropertyChangeListener(emNodeSelectionListener);
499  explorerManager = null;
500  }
501 
502  this.resultViewers.forEach((viewer) -> viewer.setNode(null));
503 
504  if (!this.isMain) {
505  this.resultViewers.forEach(DataResultViewer::clearComponent);
506  this.directoryTablePath.removeAll();
507  this.directoryTablePath = null;
508  this.numberMatchLabel.removeAll();
509  this.numberMatchLabel = null;
510  this.matchLabel.removeAll();
511  this.matchLabel = null;
512  this.setLayout(null);
513  this.removeAll();
514  this.setVisible(false);
515  }
516  }
517 
518  @Override
519  public ExplorerManager getExplorerManager() {
520  return explorerManager;
521  }
522 
526  private class ExplorerManagerNodeSelectionListener implements PropertyChangeListener {
527 
528  @Override
529  public void propertyChange(PropertyChangeEvent evt) {
530  try {
532  } catch (IllegalStateException ex) {
533  return;
534  }
535 
536  /*
537  * Only interested in node selection events.
538  */
539  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
540  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
541  try {
542  if (contentView != null) {
543  Node[] selectedNodes = explorerManager.getSelectedNodes();
544 
545  /*
546  * Pass the selected nodes to all of the result viewers
547  * sharing this explorer manager.
548  */
549  resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
550 
551  /*
552  * Passing null signals that either multiple nodes are
553  * selected, or no nodes are selected. This is important
554  * to the content view, since content views only work
555  * for a single node..
556  */
557  if (1 == selectedNodes.length) {
558  contentView.setNode(selectedNodes[0]);
559  } else {
560  contentView.setNode(null);
561  }
562  }
563  } finally {
564  setCursor(null);
565  }
566  }
567  }
568  }
569 
574  private class RootNodeListener implements NodeListener {
575 
576  private volatile boolean waitingForData = true;
577 
578  public void reset() {
579  waitingForData = true;
580  }
581 
582  @Override
583  public void childrenAdded(final NodeMemberEvent nme) {
584  Node[] delta = nme.getDelta();
585  updateMatches();
586 
587  /*
588  * There is a known issue in this code whereby we will only call
589  * setupTabs() once even though childrenAdded could be called
590  * multiple times. That means that each panel may not have access to
591  * all of the children when they decide if they support the content
592  */
593  if (waitingForData && containsReal(delta)) {
594  waitingForData = false;
595  if (SwingUtilities.isEventDispatchThread()) {
596  setupTabs(nme.getNode());
597  } else {
598  SwingUtilities.invokeLater(() -> {
599  setupTabs(nme.getNode());
600  });
601  }
602  }
603  }
604 
605  private boolean containsReal(Node[] delta) {
606  for (Node n : delta) {
607  if (!n.getDisplayName().equals(PLEASE_WAIT_NODE_DISPLAY_NAME)) {
608  return true;
609  }
610  }
611  return false;
612  }
613 
618  private void updateMatches() {
619  if (rootNode != null && rootNode.getChildren() != null) {
620  setNumMatches(rootNode.getChildren().getNodesCount());
621  }
622  }
623 
624  @Override
625  public void childrenRemoved(NodeMemberEvent nme) {
626  updateMatches();
627  }
628 
629  @Override
630  public void childrenReordered(NodeReorderEvent nre) {
631  }
632 
633  @Override
634  public void nodeDestroyed(NodeEvent ne) {
635  }
636 
637  @Override
638  public void propertyChange(PropertyChangeEvent evt) {
639  }
640  }
641 
647  @SuppressWarnings("unchecked")
648  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
649  private void initComponents() {
650 
651  directoryTablePath = new javax.swing.JLabel();
652  numberMatchLabel = new javax.swing.JLabel();
653  matchLabel = new javax.swing.JLabel();
654  dataResultTabbedPanel = new javax.swing.JTabbedPane();
655 
656  setMinimumSize(new java.awt.Dimension(0, 5));
657  setPreferredSize(new java.awt.Dimension(5, 5));
658 
659  org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N
660  directoryTablePath.setMinimumSize(new java.awt.Dimension(5, 14));
661 
662  org.openide.awt.Mnemonics.setLocalizedText(numberMatchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberMatchLabel.text")); // NOI18N
663 
664  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
665 
666  dataResultTabbedPanel.setMinimumSize(new java.awt.Dimension(0, 5));
667 
668  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
669  this.setLayout(layout);
670  layout.setHorizontalGroup(
671  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
672  .addGroup(layout.createSequentialGroup()
673  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
674  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
675  .addComponent(numberMatchLabel)
676  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
677  .addComponent(matchLabel))
678  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
679  );
680  layout.setVerticalGroup(
681  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
682  .addGroup(layout.createSequentialGroup()
683  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
684  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
685  .addComponent(numberMatchLabel)
686  .addComponent(matchLabel))
687  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
688  .addGap(0, 0, 0)
689  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
690  );
691  }// </editor-fold>//GEN-END:initComponents
692  // Variables declaration - do not modify//GEN-BEGIN:variables
693  private javax.swing.JTabbedPane dataResultTabbedPanel;
694  private javax.swing.JLabel directoryTablePath;
695  private javax.swing.JLabel matchLabel;
696  private javax.swing.JLabel numberMatchLabel;
697  // End of variables declaration//GEN-END:variables
698 
699 }
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)
DataResultPanel(boolean isMain, DataContent contentView)

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