Autopsy  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 
54 public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener {
55 
56  private ExplorerManager explorerManager;
58 
59  private Node rootNode;
60 
61  // Different DataResultsViewers
62  private final List<UpdateWrapper> viewers = new ArrayList<>();
63  //custom content viewer to send selections to, or null if the main one
65  private boolean isMain;
66  private String title;
68 
69  private static final Logger logger = Logger.getLogger(DataResultPanel.class.getName());
70  private boolean listeningToTabbedPane = false;
71  private static final String PLEASEWAIT_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class,
72  "DataResultPanel.pleasewaitNodeDisplayName");
73 
79  private DataResultPanel() {
80  this.isMain = true;
82 
83  setName(title);
84 
85  this.title = "";
86  }
87 
95  DataResultPanel(boolean isMain, String title) {
96  this();
97 
98  setName(title);
99 
100  this.isMain = isMain;
101  this.title = title;
102  }
103 
113  DataResultPanel(String title, DataContent customContentViewer) {
114  this(false, title);
115  setName(title);
116 
117  //custom content viewer tc to setup for every result viewer
118  this.customContentViewer = customContentViewer;
119  }
120 
134  public static DataResultPanel createInstance(String title, String pathText, Node givenNode, int totalMatches) {
135  DataResultPanel newDataResult = new DataResultPanel(false, title);
136 
137  createInstanceCommon(pathText, givenNode, totalMatches, newDataResult);
138  newDataResult.open();
139  return newDataResult;
140  }
141 
156  public static DataResultPanel createInstance(String title, String pathText, Node givenNode, int totalMatches, DataContent dataContent) {
157  DataResultPanel newDataResult = new DataResultPanel(title, dataContent);
158 
159  createInstanceCommon(pathText, givenNode, totalMatches, newDataResult);
160  newDataResult.open();
161  return newDataResult;
162  }
163 
179  public static DataResultPanel createInstanceUninitialized(String title, String pathText, Node givenNode, int totalMatches, DataContent dataContent) {
180  DataResultPanel newDataResult = new DataResultPanel(title, dataContent);
181 
182  createInstanceCommon(pathText, givenNode, totalMatches, newDataResult);
183  return newDataResult;
184  }
185 
194  private static void createInstanceCommon(String pathText, Node givenNode, int totalMatches, DataResultPanel newDataResult) {
195  newDataResult.numberMatchLabel.setText(Integer.toString(totalMatches));
196 
197  // set the tree table view
198  newDataResult.setNode(givenNode);
199  newDataResult.setPath(pathText);
200  }
201 
208  public void setContentViewer(DataContent customContentViewer) {
209  this.customContentViewer = customContentViewer;
210  }
211 
217  public void open() {
218  if (null == explorerManager) {
219  // Get an ExplorerManager to pass to the child DataResultViewers. If the application
220  // components are put together as expected, this will be an ExplorerManager owned
221  // by an ancestor TopComponent. The TopComponent will have put this ExplorerManager
222  // in a Lookup that is set as the action global context when the TopComponent has
223  // focus. This makes Node selections available to Actions without coupling the
224  // actions to a particular Component. Note that getting the ExplorerManager in the
225  // constructor would be too soon, since the object has no ancestor TopComponent at
226  // that point.
227  explorerManager = ExplorerManager.find(this);
228 
229  // A DataResultPanel listens for Node selections in its DataResultViewers so it
230  // can push the selections both to its child DataResultViewers and to a DataContent object.
231  // The default DataContent object is a DataContentTopComponent in the data content mode (area),
232  // and is the parent of a DataContentPanel that hosts a set of DataContentViewers.
233  emNodeSelectionListener = new ExplorerManagerNodeSelectionListener();
234  explorerManager.addPropertyChangeListener(emNodeSelectionListener);
235  }
236 
237  // Add all the DataContentViewer to the tabbed pannel.
238  // (Only when the it's opened at the first time: tabCount = 0)
239  int totalTabs = this.dataResultTabbedPanel.getTabCount();
240  if (totalTabs == 0) {
241  // @@@ Restore the implementation of DataResultViewerTable and DataResultViewerThumbnail
242  // as DataResultViewer service providers when DataResultViewers are updated
243  // to better handle the ExplorerManager sharing implemented to support actions that operate on
244  // multiple selected nodes.
245  addDataResultViewer(new DataResultViewerTable(this.explorerManager));
246  addDataResultViewer(new DataResultViewerThumbnail(this.explorerManager));
247 
248  // Find all DataResultViewer service providers and add them to the tabbed pane.
249  for (DataResultViewer factory : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
250  // @@@ Revist this isMain condition, it may be obsolete. If not,
251  // document the intent of DataResultViewer.createInstance() in the
252  // DataResultViewer interface defintion.
253  DataResultViewer drv;
254  if (isMain) {
255  //for main window, use the instance in the lookup
256  drv = factory;
257  } else {
258  //create a new instance of the viewer for non-main window
259  drv = factory.createInstance();
260  }
261  addDataResultViewer(drv);
262  }
263  }
264 
265  if (isMain) {
266  // if no node selected on DataExplorer, clear the field
267  if (rootNode == null) {
268  setNode(rootNode);
269  }
270  }
271 
272  this.setVisible(true);
273  }
274 
275  private class ExplorerManagerNodeSelectionListener implements PropertyChangeListener {
276 
277  @Override
278  public void propertyChange(PropertyChangeEvent evt) {
279  try {
281  } catch (IllegalStateException ex) {
282  // Handle the in-between condition when case is being closed
283  // and legacy selection events are pumped.
284  return;
285  }
286 
287  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
288  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
289 
290  // If a custom DataContent object has not been specified, get the default instance.
291  DataContent contentViewer = customContentViewer;
292  if (contentViewer == null) {
293  contentViewer = Lookup.getDefault().lookup(DataContent.class);
294  }
295 
296  try {
297  if (contentViewer != null) {
298  Node[] selectedNodes = explorerManager.getSelectedNodes();
299  for (UpdateWrapper drv : viewers) {
300  drv.setSelectedNodes(selectedNodes);
301  }
302 
303  // Passing null signals that either multiple nodes are selected, or no nodes are selected.
304  // This is important to the DataContent object, since the content mode (area) of the app is designed
305  // to show only the content underlying a single Node.
306  if (selectedNodes.length == 1) {
307  contentViewer.setNode(selectedNodes[0]);
308  } else {
309  contentViewer.setNode(null);
310  }
311  }
312  } finally {
313  setCursor(null);
314  }
315  }
316  }
317  }
318 
319  private void addDataResultViewer(DataResultViewer dataResultViewer) {
320  UpdateWrapper viewerWrapper = new UpdateWrapper(dataResultViewer);
321  if (null != this.customContentViewer) {
322  viewerWrapper.setContentViewer(this.customContentViewer);
323  }
324  this.viewers.add(viewerWrapper);
325  this.dataResultTabbedPanel.addTab(dataResultViewer.getTitle(), dataResultViewer.getComponent());
326  }
327 
333  void close() {
334 
335  if (null != explorerManager && null != emNodeSelectionListener) {
336  explorerManager.removePropertyChangeListener(emNodeSelectionListener);
337  explorerManager = null;
338  }
339 
340  // clear all set nodes
341  for (UpdateWrapper drv : this.viewers) {
342  drv.setNode(null);
343  }
344 
345  if (!this.isMain) {
346  for (UpdateWrapper drv : this.viewers) {
347  drv.clearComponent();
348  }
349  this.directoryTablePath.removeAll();
350  this.directoryTablePath = null;
351  this.numberMatchLabel.removeAll();
352  this.numberMatchLabel = null;
353  this.matchLabel.removeAll();
354  this.matchLabel = null;
355  this.setLayout(null);
356  this.removeAll();
357  this.setVisible(false);
358  }
359  }
360 
361  @Override
362  public String getPreferredID() {
363  return getName();
364  }
365 
366  @Override
367  public void setNode(Node selectedNode) {
368  if (this.rootNode != null) {
369  this.rootNode.removeNodeListener(rootNodeListener);
370  }
371  // Deferring becoming a listener to the tabbed pane until this point
372  // eliminates handling a superfluous stateChanged event during construction.
373  if (listeningToTabbedPane == false) {
374  dataResultTabbedPanel.addChangeListener(this);
375  listeningToTabbedPane = true;
376  }
377 
378  this.rootNode = selectedNode;
379  if (this.rootNode != null) {
380  rootNodeListener.reset();
381  this.rootNode.addNodeListener(rootNodeListener);
382  }
383 
384  resetTabs(selectedNode);
385  setupTabs(selectedNode);
386 
387  if (selectedNode != null) {
388  int childrenCount = selectedNode.getChildren().getNodesCount();
389  this.numberMatchLabel.setText(Integer.toString(childrenCount));
390  }
391  this.numberMatchLabel.setVisible(true);
392  }
393 
394  private void setupTabs(Node selectedNode) {
395  //update/disable tabs based on if supported for this node
396  int drvC = 0;
397  for (UpdateWrapper drv : viewers) {
398 
399  if (drv.isSupported(selectedNode)) {
400  dataResultTabbedPanel.setEnabledAt(drvC, true);
401  } else {
402  dataResultTabbedPanel.setEnabledAt(drvC, false);
403  }
404  ++drvC;
405  }
406 
407  // if the current tab is no longer enabled, then find one that is
408  boolean hasViewerEnabled = true;
409  int currentActiveTab = dataResultTabbedPanel.getSelectedIndex();
410  if ((currentActiveTab == -1) || (dataResultTabbedPanel.isEnabledAt(currentActiveTab) == false)) {
411  hasViewerEnabled = false;
412  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) {
413  if (dataResultTabbedPanel.isEnabledAt(i)) {
414  currentActiveTab = i;
415  hasViewerEnabled = true;
416  break;
417  }
418  }
419 
420  if (hasViewerEnabled) {
421  dataResultTabbedPanel.setSelectedIndex(currentActiveTab);
422  }
423  }
424 
425  if (hasViewerEnabled) {
426  viewers.get(currentActiveTab).setNode(selectedNode);
427  }
428  }
429 
430  @Override
431  public void setTitle(String title) {
432  setName(title);
433 
434  }
435 
436  @Override
437  public void setPath(String pathText) {
438  this.directoryTablePath.setText(pathText);
439  }
440 
441  @Override
442  public boolean isMain() {
443  return this.isMain;
444  }
445 
446  @Override
447  public List<DataResultViewer> getViewers() {
448  List<DataResultViewer> ret = new ArrayList<>();
449  for (UpdateWrapper w : viewers) {
450  ret.add(w.getViewer());
451  }
452 
453  return ret;
454  }
455 
456  public boolean canClose() {
457  /*
458  * If this is the main results panel in the main top component in the
459  * upper right of the main window, only allow it to be closed when
460  * there's no case opened or no data sources in the open case.
461  */
462  return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false;
463  }
464 
465  @Override
466  public void stateChanged(ChangeEvent e) {
467  JTabbedPane pane = (JTabbedPane) e.getSource();
468 
469  // Get and set current selected tab
470  int currentTab = pane.getSelectedIndex();
471  if (currentTab != -1) {
472  UpdateWrapper drv = this.viewers.get(currentTab);
473  // @@@ Restore commented out isOutDated() check after DataResultViewers are updated
474  // to better handle the ExplorerManager sharing implemented to support actions that operate on
475  // multiple selected nodes.
476  //if (drv.isOutdated()) {
477  // change the cursor to "waiting cursor" for this operation
478  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
479  try {
480  drv.setNode(rootNode);
481  } finally {
482  this.setCursor(null);
483  }
484  //}
485  }
486  }
487 
498  public void resetTabs(Node selectedNode) {
499 
500  for (UpdateWrapper drv : this.viewers) {
501  drv.resetComponent();
502  }
503  }
504 
505  public void setSelectedNodes(Node[] selected) {
506  for (UpdateWrapper drv : this.viewers) {
507  drv.setSelectedNodes(selected);
508  }
509  }
510 
511  public Node getRootNode() {
512  return this.rootNode;
513  }
514 
520  @SuppressWarnings("unchecked")
521  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
522  private void initComponents() {
523 
524  directoryTablePath = new javax.swing.JLabel();
525  numberMatchLabel = new javax.swing.JLabel();
526  matchLabel = new javax.swing.JLabel();
527  dataResultTabbedPanel = new javax.swing.JTabbedPane();
528 
529  setMinimumSize(new java.awt.Dimension(0, 5));
530  setPreferredSize(new java.awt.Dimension(5, 5));
531 
532  org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N
533  directoryTablePath.setMinimumSize(new java.awt.Dimension(5, 14));
534 
535  org.openide.awt.Mnemonics.setLocalizedText(numberMatchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberMatchLabel.text")); // NOI18N
536 
537  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
538 
539  dataResultTabbedPanel.setMinimumSize(new java.awt.Dimension(0, 5));
540 
541  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
542  this.setLayout(layout);
543  layout.setHorizontalGroup(
544  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
545  .addGroup(layout.createSequentialGroup()
546  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
547  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
548  .addComponent(numberMatchLabel)
549  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
550  .addComponent(matchLabel))
551  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
552  );
553  layout.setVerticalGroup(
554  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
555  .addGroup(layout.createSequentialGroup()
556  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
557  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
558  .addComponent(numberMatchLabel)
559  .addComponent(matchLabel))
560  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
561  .addGap(0, 0, 0)
562  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
563  );
564  }// </editor-fold>//GEN-END:initComponents
565  // Variables declaration - do not modify//GEN-BEGIN:variables
566  private javax.swing.JTabbedPane dataResultTabbedPanel;
567  private javax.swing.JLabel directoryTablePath;
568  private javax.swing.JLabel matchLabel;
569  private javax.swing.JLabel numberMatchLabel;
570  // End of variables declaration//GEN-END:variables
571 
572  private static class UpdateWrapper {
573 
575  private boolean outdated;
576 
578  this.wrapped = wrapped;
579  this.outdated = true;
580  }
581 
582  DataResultViewer getViewer() {
583  return wrapped;
584  }
585 
586  void setNode(Node selectedNode) {
587  this.wrapped.setNode(selectedNode);
588  this.outdated = false;
589  }
590 
591  void resetComponent() {
592  this.wrapped.resetComponent();
593  this.outdated = true;
594  }
595 
596  void clearComponent() {
597  this.wrapped.clearComponent();
598  this.outdated = true;
599  }
600 
601  boolean isOutdated() {
602  return this.outdated;
603  }
604 
605  void setSelectedNodes(Node[] selected) {
606  this.wrapped.setSelectedNodes(selected);
607  }
608 
609  boolean isSupported(Node selectedNode) {
610  return this.wrapped.isSupported(selectedNode);
611  }
612 
613  void setContentViewer(DataContent contentViewer) {
614  this.wrapped.setContentViewer(contentViewer);
615  }
616  }
617 
623  public void setNumMatches(Integer numMatches) {
624  if (this.numberMatchLabel != null) {
625  this.numberMatchLabel.setText(Integer.toString(numMatches));
626  }
627  }
628 
629  private class RootNodeListener implements NodeListener {
630 
631  private volatile boolean waitingForData = true;
632 
633  public void reset() {
634  waitingForData = true;
635  }
636 
637  @Override
638  public void childrenAdded(final NodeMemberEvent nme) {
639  Node[] delta = nme.getDelta();
640  updateMatches();
641 
642  /*
643  * There is a known issue in this code whereby we will only call
644  * setupTabs() once even though childrenAdded could be called
645  * multiple times. That means that each panel may not have access to
646  * all of the children when they decide if they support the content
647  */
648  if (waitingForData && containsReal(delta)) {
649  waitingForData = false;
650  if (SwingUtilities.isEventDispatchThread()) {
651  setupTabs(nme.getNode());
652  } else {
653  SwingUtilities.invokeLater(new Runnable() {
654  @Override
655  public void run() {
656  setupTabs(nme.getNode());
657  }
658  });
659  }
660  }
661  }
662 
663  private boolean containsReal(Node[] delta) {
664  for (Node n : delta) {
665  if (!n.getDisplayName().equals(PLEASEWAIT_NODE_DISPLAY_NAME)) {
666  return true;
667  }
668  }
669  return false;
670  }
671 
676  private void updateMatches() {
677  if (rootNode != null && rootNode.getChildren() != null) {
678  setNumMatches(rootNode.getChildren().getNodesCount());
679  }
680  }
681 
682  @Override
683  public void childrenRemoved(NodeMemberEvent nme) {
684  updateMatches();
685  }
686 
687  @Override
688  public void childrenReordered(NodeReorderEvent nre) {
689  }
690 
691  @Override
692  public void nodeDestroyed(NodeEvent ne) {
693  }
694 
695  @Override
696  public void propertyChange(PropertyChangeEvent evt) {
697  }
698  }
699 }
static void createInstanceCommon(String pathText, Node givenNode, int totalMatches, DataResultPanel newDataResult)
static DataResultPanel createInstanceUninitialized(String title, String pathText, Node givenNode, int totalMatches, DataContent dataContent)
static DataResultPanel createInstance(String title, String pathText, Node givenNode, int totalMatches, DataContent dataContent)
void setContentViewer(DataContent customContentViewer)
ExplorerManagerNodeSelectionListener emNodeSelectionListener
void addDataResultViewer(DataResultViewer dataResultViewer)
static DataResultPanel createInstance(String title, String pathText, Node givenNode, int totalMatches)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

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