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-2016 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 DUMMY_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultPanel.class,
72  "DataResultPanel.dummyNodeDisplayName");
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  if (!Case.isCaseOpen()) {
280  // Handle the in-between condition when case is being closed
281  // and legacy selection events are pumped.
282  return;
283  }
284 
285  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
286  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
287 
288  // If a custom DataContent object has not been specified, get the default instance.
289  DataContent contentViewer = customContentViewer;
290  if (contentViewer == null) {
291  contentViewer = Lookup.getDefault().lookup(DataContent.class);
292  }
293 
294  try {
295  if (contentViewer != null) {
296  Node[] selectedNodes = explorerManager.getSelectedNodes();
297  for (UpdateWrapper drv : viewers) {
298  drv.setSelectedNodes(selectedNodes);
299  }
300 
301  // Passing null signals that either multiple nodes are selected, or no nodes are selected.
302  // This is important to the DataContent object, since the content mode (area) of the app is designed
303  // to show only the content underlying a single Node.
304  if (selectedNodes.length == 1) {
305  contentViewer.setNode(selectedNodes[0]);
306  } else {
307  contentViewer.setNode(null);
308  }
309  }
310  } finally {
311  setCursor(null);
312  }
313  }
314  }
315  }
316 
317  private void addDataResultViewer(DataResultViewer dataResultViewer) {
318  UpdateWrapper viewerWrapper = new UpdateWrapper(dataResultViewer);
319  if (null != this.customContentViewer) {
320  viewerWrapper.setContentViewer(this.customContentViewer);
321  }
322  this.viewers.add(viewerWrapper);
323  this.dataResultTabbedPanel.addTab(dataResultViewer.getTitle(), dataResultViewer.getComponent());
324  }
325 
331  void close() {
332 
333  if (null != explorerManager && null != emNodeSelectionListener) {
334  explorerManager.removePropertyChangeListener(emNodeSelectionListener);
335  explorerManager = null;
336  }
337 
338  // clear all set nodes
339  for (UpdateWrapper drv : this.viewers) {
340  drv.setNode(null);
341  }
342 
343  if (!this.isMain) {
344  for (UpdateWrapper drv : this.viewers) {
345  drv.clearComponent();
346  }
347  this.directoryTablePath.removeAll();
348  this.directoryTablePath = null;
349  this.numberMatchLabel.removeAll();
350  this.numberMatchLabel = null;
351  this.matchLabel.removeAll();
352  this.matchLabel = null;
353  this.setLayout(null);
354  this.removeAll();
355  this.setVisible(false);
356  }
357  }
358 
359  @Override
360  public String getPreferredID() {
361  return getName();
362  }
363 
364  @Override
365  public void setNode(Node selectedNode) {
366  if (this.rootNode != null) {
367  this.rootNode.removeNodeListener(rootNodeListener);
368  }
369  // Deferring becoming a listener to the tabbed pane until this point
370  // eliminates handling a superfluous stateChanged event during construction.
371  if (listeningToTabbedPane == false) {
372  dataResultTabbedPanel.addChangeListener(this);
373  listeningToTabbedPane = true;
374  }
375 
376  this.rootNode = selectedNode;
377  if (this.rootNode != null) {
378  rootNodeListener.reset();
379  this.rootNode.addNodeListener(rootNodeListener);
380  }
381 
382  resetTabs(selectedNode);
383  setupTabs(selectedNode);
384 
385  if (selectedNode != null) {
386  int childrenCount = selectedNode.getChildren().getNodesCount();
387  this.numberMatchLabel.setText(Integer.toString(childrenCount));
388  }
389  this.numberMatchLabel.setVisible(true);
390  }
391 
392  private void setupTabs(Node selectedNode) {
393  //update/disable tabs based on if supported for this node
394  int drvC = 0;
395  for (UpdateWrapper drv : viewers) {
396 
397  if (drv.isSupported(selectedNode)) {
398  dataResultTabbedPanel.setEnabledAt(drvC, true);
399  } else {
400  dataResultTabbedPanel.setEnabledAt(drvC, false);
401  }
402  ++drvC;
403  }
404 
405  // if the current tab is no longer enabled, then find one that is
406  boolean hasViewerEnabled = true;
407  int currentActiveTab = dataResultTabbedPanel.getSelectedIndex();
408  if ((currentActiveTab == -1) || (dataResultTabbedPanel.isEnabledAt(currentActiveTab) == false)) {
409  hasViewerEnabled = false;
410  for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) {
411  if (dataResultTabbedPanel.isEnabledAt(i)) {
412  currentActiveTab = i;
413  hasViewerEnabled = true;
414  break;
415  }
416  }
417 
418  if (hasViewerEnabled) {
419  dataResultTabbedPanel.setSelectedIndex(currentActiveTab);
420  }
421  }
422 
423  if (hasViewerEnabled) {
424  viewers.get(currentActiveTab).setNode(selectedNode);
425  }
426  }
427 
428  @Override
429  public void setTitle(String title) {
430  setName(title);
431 
432  }
433 
434  @Override
435  public void setPath(String pathText) {
436  this.directoryTablePath.setText(pathText);
437  }
438 
439  @Override
440  public boolean isMain() {
441  return this.isMain;
442  }
443 
444  @Override
445  public List<DataResultViewer> getViewers() {
446  List<DataResultViewer> ret = new ArrayList<DataResultViewer>();
447  for (UpdateWrapper w : viewers) {
448  ret.add(w.getViewer());
449  }
450 
451  return ret;
452  }
453 
454  public boolean canClose() {
455  return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false; // only allow this window to be closed when there's no case opened or no image in this case
456  }
457 
458  @Override
459  public void stateChanged(ChangeEvent e) {
460  JTabbedPane pane = (JTabbedPane) e.getSource();
461 
462  // Get and set current selected tab
463  int currentTab = pane.getSelectedIndex();
464  if (currentTab != -1) {
465  UpdateWrapper drv = this.viewers.get(currentTab);
466  // @@@ Restore commented out isOutDated() check after DataResultViewers are updated
467  // to better handle the ExplorerManager sharing implemented to support actions that operate on
468  // multiple selected nodes.
469  //if (drv.isOutdated()) {
470  // change the cursor to "waiting cursor" for this operation
471  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
472  try {
473  drv.setNode(rootNode);
474  } finally {
475  this.setCursor(null);
476  }
477  //}
478  }
479  }
480 
491  public void resetTabs(Node selectedNode) {
492 
493  for (UpdateWrapper drv : this.viewers) {
494  drv.resetComponent();
495  }
496  }
497 
498  public void setSelectedNodes(Node[] selected) {
499  for (UpdateWrapper drv : this.viewers) {
500  drv.setSelectedNodes(selected);
501  }
502  }
503 
504  public Node getRootNode() {
505  return this.rootNode;
506  }
507 
513  @SuppressWarnings("unchecked")
514  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
515  private void initComponents() {
516 
517  directoryTablePath = new javax.swing.JLabel();
518  numberMatchLabel = new javax.swing.JLabel();
519  matchLabel = new javax.swing.JLabel();
520  dataResultTabbedPanel = new javax.swing.JTabbedPane();
521 
522  setMinimumSize(new java.awt.Dimension(0, 5));
523  setPreferredSize(new java.awt.Dimension(5, 5));
524 
525  org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N
526  directoryTablePath.setMinimumSize(new java.awt.Dimension(5, 14));
527 
528  org.openide.awt.Mnemonics.setLocalizedText(numberMatchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberMatchLabel.text")); // NOI18N
529 
530  org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
531 
532  dataResultTabbedPanel.setMinimumSize(new java.awt.Dimension(0, 5));
533 
534  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
535  this.setLayout(layout);
536  layout.setHorizontalGroup(
537  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
538  .addGroup(layout.createSequentialGroup()
539  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
540  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
541  .addComponent(numberMatchLabel)
542  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
543  .addComponent(matchLabel))
544  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
545  );
546  layout.setVerticalGroup(
547  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
548  .addGroup(layout.createSequentialGroup()
549  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
550  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
551  .addComponent(numberMatchLabel)
552  .addComponent(matchLabel))
553  .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
554  .addGap(0, 0, 0)
555  .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
556  );
557  }// </editor-fold>//GEN-END:initComponents
558  // Variables declaration - do not modify//GEN-BEGIN:variables
559  private javax.swing.JTabbedPane dataResultTabbedPanel;
560  private javax.swing.JLabel directoryTablePath;
561  private javax.swing.JLabel matchLabel;
562  private javax.swing.JLabel numberMatchLabel;
563  // End of variables declaration//GEN-END:variables
564 
565  private static class UpdateWrapper {
566 
568  private boolean outdated;
569 
571  this.wrapped = wrapped;
572  this.outdated = true;
573  }
574 
575  DataResultViewer getViewer() {
576  return wrapped;
577  }
578 
579  void setNode(Node selectedNode) {
580  this.wrapped.setNode(selectedNode);
581  this.outdated = false;
582  }
583 
584  void resetComponent() {
585  this.wrapped.resetComponent();
586  this.outdated = true;
587  }
588 
589  void clearComponent() {
590  this.wrapped.clearComponent();
591  this.outdated = true;
592  }
593 
594  boolean isOutdated() {
595  return this.outdated;
596  }
597 
598  void setSelectedNodes(Node[] selected) {
599  this.wrapped.setSelectedNodes(selected);
600  }
601 
602  boolean isSupported(Node selectedNode) {
603  return this.wrapped.isSupported(selectedNode);
604  }
605 
606  void setContentViewer(DataContent contentViewer) {
607  this.wrapped.setContentViewer(contentViewer);
608  }
609  }
610 
616  public void setNumMatches(Integer numMatches) {
617  if (this.numberMatchLabel != null) {
618  this.numberMatchLabel.setText(Integer.toString(numMatches));
619  }
620  }
621 
622  private class RootNodeListener implements NodeListener {
623 
624  private volatile boolean waitingForData = true;
625 
626  public void reset() {
627  waitingForData = true;
628  }
629 
630  @Override
631  public void childrenAdded(final NodeMemberEvent nme) {
632  Node[] delta = nme.getDelta();
633  updateMatches();
634 
635  /*
636  * There is a known issue in this code whereby we will only call
637  * setupTabs() once even though childrenAdded could be called
638  * multiple times. That means that each panel may not have access to
639  * all of the children when they decide if they support the content
640  */
641  if (waitingForData && containsReal(delta)) {
642  waitingForData = false;
643  if (SwingUtilities.isEventDispatchThread()) {
644  setupTabs(nme.getNode());
645  } else {
646  SwingUtilities.invokeLater(new Runnable() {
647  @Override
648  public void run() {
649  setupTabs(nme.getNode());
650  }
651  });
652  }
653  }
654  }
655 
656  private boolean containsReal(Node[] delta) {
657  for (Node n : delta) {
658  if (!n.getDisplayName().equals(DUMMY_NODE_DISPLAY_NAME)) {
659  return true;
660  }
661  }
662  return false;
663  }
664 
669  private void updateMatches() {
670  if (rootNode != null && rootNode.getChildren() != null) {
671  setNumMatches(rootNode.getChildren().getNodesCount());
672  }
673  }
674 
675  @Override
676  public void childrenRemoved(NodeMemberEvent nme) {
677  updateMatches();
678  }
679 
680  @Override
681  public void childrenReordered(NodeReorderEvent nre) {
682  }
683 
684  @Override
685  public void nodeDestroyed(NodeEvent ne) {
686  }
687 
688  @Override
689  public void propertyChange(PropertyChangeEvent evt) {
690  }
691  }
692 }
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: Tue Oct 25 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.