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

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.