Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DirectoryTreeTopComponent.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.directorytree;
20 
22 import java.awt.Cursor;
23 import java.awt.EventQueue;
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyChangeListener;
26 import java.beans.PropertyVetoException;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.HashSet;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.logging.Level;
34 import java.util.prefs.PreferenceChangeEvent;
35 import java.util.prefs.PreferenceChangeListener;
36 import javax.swing.Action;
37 import javax.swing.SwingUtilities;
38 import javax.swing.tree.TreeSelectionModel;
39 import org.openide.explorer.ExplorerManager;
40 import org.openide.explorer.ExplorerUtils;
41 import org.openide.explorer.view.BeanTreeView;
42 import org.openide.explorer.view.TreeView;
43 import org.openide.nodes.AbstractNode;
44 import org.openide.nodes.Children;
45 import org.openide.nodes.Node;
46 import org.openide.nodes.NodeNotFoundException;
47 import org.openide.nodes.NodeOp;
48 import org.openide.util.NbBundle;
49 import org.openide.windows.TopComponent;
50 import org.openide.windows.WindowManager;
79 import org.sleuthkit.datamodel.BlackboardArtifact;
80 import org.sleuthkit.datamodel.BlackboardAttribute;
81 import org.sleuthkit.datamodel.Content;
82 import org.sleuthkit.datamodel.SleuthkitCase;
83 import org.sleuthkit.datamodel.TskCoreException;
84 import org.sleuthkit.datamodel.TskException;
85 
89 // Registered as a service provider for DataExplorer in layer.xml
90 public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider, BlackboardResultViewer {
91 
92  private final transient ExplorerManager em = new ExplorerManager();
94  private final DataResultTopComponent dataResult = new DataResultTopComponent(true, NbBundle.getMessage(this.getClass(),
95  "DirectoryTreeTopComponent.title.text"));
96  private final LinkedList<String[]> backList;
97  private final LinkedList<String[]> forwardList;
98  private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
99  private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
101 
106  initComponents();
107 
108  // only allow one item to be selected at a time
109  ((BeanTreeView) jScrollPane1).setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
110  // remove the close button
111  putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
112  setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
113  setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
114 
116  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
117 
118  // set the back & forward list and also disable the back & forward button
119  this.backList = new LinkedList<>();
120  this.forwardList = new LinkedList<>();
121  backButton.setEnabled(false);
122  forwardButton.setEnabled(false);
123  }
124 
128  private void subscribeToChangeEvents() {
129  UserPreferences.addChangeListener(new PreferenceChangeListener() {
130  @Override
131  public void preferenceChange(PreferenceChangeEvent evt) {
132  switch (evt.getKey()) {
136  break;
139  // TODO: Need a way to refresh the Views subtree
140  break;
141  }
142  }
143  });
144  Case.addEventSubscriber(new HashSet<>(Arrays.asList(Case.Events.CURRENT_CASE.toString(), Case.Events.DATA_SOURCE_ADDED.toString())), this);
145  this.em.addPropertyChangeListener(this);
148  }
149 
151  this.dataResult.requestActive();
152  }
153 
154  public void openDirectoryListing() {
155  this.dataResult.open();
156  }
157 
159  return this.dataResult;
160  }
161 
167  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
168  private void initComponents() {
169 
170  jScrollPane1 = new BeanTreeView();
171  backButton = new javax.swing.JButton();
172  forwardButton = new javax.swing.JButton();
173  showRejectedCheckBox = new javax.swing.JCheckBox();
174 
175  jScrollPane1.setBorder(null);
176 
177  backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
178  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
179  backButton.setBorderPainted(false);
180  backButton.setContentAreaFilled(false);
181  backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
182  backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
183  backButton.setMaximumSize(new java.awt.Dimension(55, 100));
184  backButton.setMinimumSize(new java.awt.Dimension(5, 5));
185  backButton.setPreferredSize(new java.awt.Dimension(23, 23));
186  backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
187  backButton.addActionListener(new java.awt.event.ActionListener() {
188  public void actionPerformed(java.awt.event.ActionEvent evt) {
190  }
191  });
192 
193  forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
194  org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
195  forwardButton.setBorderPainted(false);
196  forwardButton.setContentAreaFilled(false);
197  forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
198  forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
199  forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
200  forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
201  forwardButton.setPreferredSize(new java.awt.Dimension(23, 23));
202  forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
203  forwardButton.addActionListener(new java.awt.event.ActionListener() {
204  public void actionPerformed(java.awt.event.ActionEvent evt) {
206  }
207  });
208 
209  org.openide.awt.Mnemonics.setLocalizedText(showRejectedCheckBox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showRejectedCheckBox.text")); // NOI18N
210 
211  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
212  this.setLayout(layout);
213  layout.setHorizontalGroup(
214  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
215  .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
216  .addGroup(layout.createSequentialGroup()
217  .addGap(5, 5, 5)
218  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
219  .addGap(0, 0, 0)
220  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
221  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 46, Short.MAX_VALUE)
222  .addComponent(showRejectedCheckBox)
223  .addContainerGap())
224  );
225  layout.setVerticalGroup(
226  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
227  .addGroup(layout.createSequentialGroup()
228  .addGap(5, 5, 5)
229  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
230  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
231  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
232  .addComponent(showRejectedCheckBox))
233  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
234  .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 838, javax.swing.GroupLayout.PREFERRED_SIZE)
235  .addContainerGap())
236  );
237  }// </editor-fold>//GEN-END:initComponents
238 
239  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
240  // change the cursor to "waiting cursor" for this operation
241  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
242 
243  // the end is the current place,
244  String[] currentNodePath = backList.pollLast();
245  forwardList.addLast(currentNodePath);
246  forwardButton.setEnabled(true);
247 
248  /*
249  * We peek instead of poll because we use its existence in the list
250  * later on so that we do not reset the forward list after the selection
251  * occurs.
252  */
253  String[] newCurrentNodePath = backList.peekLast();
254 
255  // enable / disable the back and forward button
256  if (backList.size() > 1) {
257  backButton.setEnabled(true);
258  } else {
259  backButton.setEnabled(false);
260  }
261 
262  // update the selection on directory tree
263  setSelectedNode(newCurrentNodePath, null);
264 
265  this.setCursor(null);
266  }//GEN-LAST:event_backButtonActionPerformed
267 
268  private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
269  // change the cursor to "waiting cursor" for this operation
270  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
271 
272  String[] newCurrentNodePath = forwardList.pollLast();
273  if (!forwardList.isEmpty()) {
274  forwardButton.setEnabled(true);
275  } else {
276  forwardButton.setEnabled(false);
277  }
278 
279  backList.addLast(newCurrentNodePath);
280  backButton.setEnabled(true);
281 
282  // update the selection on directory tree
283  setSelectedNode(newCurrentNodePath, null);
284 
285  this.setCursor(null);
286  }//GEN-LAST:event_forwardButtonActionPerformed
287 
288  // Variables declaration - do not modify//GEN-BEGIN:variables
289  private javax.swing.JButton backButton;
290  private javax.swing.JButton forwardButton;
291  private javax.swing.JScrollPane jScrollPane1;
292  private javax.swing.JCheckBox showRejectedCheckBox;
293  // End of variables declaration//GEN-END:variables
294 
301  public static synchronized DirectoryTreeTopComponent getDefault() {
302  if (instance == null) {
303  instance = new DirectoryTreeTopComponent();
304  }
305  return instance;
306  }
307 
312  public static synchronized DirectoryTreeTopComponent findInstance() {
313  WindowManager winManager = WindowManager.getDefault();
314  TopComponent win = winManager.findTopComponent(PREFERRED_ID);
315  if (win == null) {
316  LOGGER.warning(
317  "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
318  return getDefault();
319  }
320  if (win instanceof DirectoryTreeTopComponent) {
321  return (DirectoryTreeTopComponent) win;
322  }
323  LOGGER.warning(
324  "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
325  + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
326  return getDefault();
327  }
328 
335  @Override
336  public int getPersistenceType() {
337  return TopComponent.PERSISTENCE_NEVER;
338  }
339 
347  @Override
348  public void componentOpened() {
349  // change the cursor to "waiting cursor" for this operation
350  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
351  try {
352  if (Case.isCaseOpen()) {
353  Case currentCase = Case.getCurrentCase();
354 
355  // close the top component if there's no image in this case
356  if (currentCase.hasData() == false) {
357  //this.close();
358  ((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root
359  } else {
360  // if there's at least one image, load the image and open the top component
361  List<Object> items = new ArrayList<>();
362  final SleuthkitCase tskCase = currentCase.getSleuthkitCase();
363  items.add(new DataSources());
364  items.add(new Views(tskCase));
365  items.add(new Results(tskCase));
366  items.add(new Tags());
367  items.add(new Reports());
368  contentChildren = new RootContentChildren(items);
369 
370  Node root = new AbstractNode(contentChildren) {
375  @Override
376  public Action[] getActions(boolean popup) {
377  return new Action[]{};
378  }
379 
380  // Overide the AbstractNode use of DefaultHandle to return
381  // a handle which can be serialized without a parent
382  @Override
383  public Node.Handle getHandle() {
384  return new Node.Handle() {
385  @Override
386  public Node getNode() throws IOException {
387  return em.getRootContext();
388  }
389  };
390  }
391  };
392 
393  root = new DirectoryTreeFilterNode(root, true);
394 
395  em.setRootContext(root);
396  em.getRootContext().setName(currentCase.getName());
397  em.getRootContext().setDisplayName(currentCase.getName());
398  ((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root
399 
400  // Reset the forward and back lists because we're resetting the root context
401  resetHistory();
402 
403  Children childNodes = em.getRootContext().getChildren();
404  TreeView tree = getTree();
405 
406  Node results = childNodes.findChild(ResultsNode.NAME);
407  tree.expandNode(results);
408 
409  Children resultsChilds = results.getChildren();
410  tree.expandNode(resultsChilds.findChild(KeywordHits.NAME));
411  tree.expandNode(resultsChilds.findChild(ExtractedContent.NAME));
412 
413  Accounts accounts = resultsChilds.findChild(Accounts.NAME).getLookup().lookup(Accounts.class);
415  showRejectedCheckBox.setSelected(false);
416 
417  Node views = childNodes.findChild(ViewsNode.NAME);
418  Children viewsChilds = views.getChildren();
419  for (Node n : viewsChilds.getNodes()) {
420  tree.expandNode(n);
421  }
422 
423  tree.collapseNode(views);
424 
425  // if the dataResult is not opened
426  if (!dataResult.isOpened()) {
427  dataResult.open(); // open the data result top component as well when the directory tree is opened
428  }
429 
430  // select the first image node, if there is one
431  // (this has to happen after dataResult is opened, because the event
432  // of changing the selected node fires a handler that tries to make
433  // dataResult active)
434  if (childNodes.getNodesCount() > 0) {
435  try {
436  em.setSelectedNodes(new Node[]{childNodes.getNodeAt(0)});
437  } catch (Exception ex) {
438  LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
439  }
440  }
441 
442  }
443  }
444  } finally {
445  this.setCursor(null);
446  }
447  }
448 
455  @Override
456  public void componentClosed() {
457  //@@@ push the selection node to null?
458  contentChildren = null;
459  }
460 
461  void writeProperties(java.util.Properties p) {
462  // better to version settings since initial version as advocated at
463  // http://wiki.apidesign.org/wiki/PropertyFiles
464  p.setProperty("version", "1.0");
465  // TODO store your settings
466  }
467 
468  Object readProperties(java.util.Properties p) {
469  if (instance == null) {
470  instance = this;
471  }
472  instance.readPropertiesImpl(p);
473  return instance;
474  }
475 
476  private void readPropertiesImpl(java.util.Properties p) {
477  String version = p.getProperty("version");
478  // TODO read your settings according to their version
479  }
480 
486  @Override
487  protected String preferredID() {
488  return PREFERRED_ID;
489  }
490 
491  @Override
492  public boolean canClose() {
493  return !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
494  }
495 
501  @Override
502  public ExplorerManager getExplorerManager() {
503  return this.em;
504  }
505 
511  @Override
512  public Action[] getActions() {
513  return new Action[]{};
514  }
515 
521  public Node getSelectedNode() {
522  Node result = null;
523 
524  Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
525  if (selectedNodes.length > 0) {
526  result = selectedNodes[0];
527  }
528  return result;
529  }
530 
537  @Override
538  public void propertyChange(PropertyChangeEvent evt) {
540  String changed = evt.getPropertyName();
541  if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
542  // When a case is closed, the old value of this property is the
543  // closed Case object and the new value is null. When a case is
544  // opened, the old value is null and the new value is the new Case
545  // object.
546  // @@@ This needs to be revisited. Perhaps case closed and case
547  // opened events instead of property change events would be a better
548  // solution. Either way, more probably needs to be done to clean up
549  // data model objects when a case is closed.
550  if (evt.getOldValue() != null && evt.getNewValue() == null) {
551  // The current case has been closed. Reset the ExplorerManager.
552  SwingUtilities.invokeLater(() -> {
553  Node emptyNode = new AbstractNode(Children.LEAF);
554  em.setRootContext(emptyNode);
555  });
556  } else if (evt.getNewValue() != null) {
557  // A new case has been opened. Reset the ExplorerManager.
558  Case newCase = (Case) evt.getNewValue();
559  final String newCaseName = newCase.getName();
560  SwingUtilities.invokeLater(() -> {
561  em.getRootContext().setName(newCaseName);
562  em.getRootContext().setDisplayName(newCaseName);
563 
564  // Reset the forward and back
565  // buttons. Note that a call to CoreComponentControl.openCoreWindows()
566  // by the new Case object will lead to a componentOpened() call
567  // that will repopulate the tree.
568  // @@@ The repopulation of the tree in this fashion also merits
569  // reconsideration.
570  resetHistory();
571  });
572  }
573  } // if the image is added to the case
574  else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
581  try {
582  Case currentCase = Case.getCurrentCase();
583  // We only need to trigger openCoreWindows() when the
584  // first data source is added.
585  if (currentCase.getDataSources().size() == 1) {
586  SwingUtilities.invokeLater(() -> {
588  });
589  }
590  } catch (IllegalStateException | TskCoreException notUsed) {
594  }
595  } // change in node selection
596  else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
597  SwingUtilities.invokeLater(() -> {
598  respondSelection((Node[]) evt.getOldValue(), (Node[]) evt.getNewValue());
599  });
600  } else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
601  // nothing to do here.
602  // all nodes should be listening for these events and update accordingly.
603  }
604  }
605  }
606 
607  @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
616  private void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
617  if (!Case.isCaseOpen()) {
618  //handle in-between condition when case is being closed
619  //and legacy selection events are pumped
620  return;
621  }
622 
623  // Some lock that prevents certain Node operations is set during the
624  // ExplorerManager selection-change, so we must handle changes after the
625  // selection-change event is processed.
626  //TODO find a different way to refresh data result viewer, scheduling this
627  //to EDT breaks loading of nodes in the background
628  EventQueue.invokeLater(new Runnable() {
629  @Override
630  public void run() {
631  // change the cursor to "waiting cursor" for this operation
632  DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
633  try {
634 
635  Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
636  if (treeNode != null) {
637  DirectoryTreeFilterNode.OriginalNode origin = treeNode.getLookup().lookup(DirectoryTreeFilterNode.OriginalNode.class);
638  if (origin == null) {
639  return;
640  }
641 
642  Node originNode = origin.getNode();
643 
644  //set node, wrap in filter node first to filter out children
645  Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
646  Node kffn = new KnownFileFilterNode(drfn, KnownFileFilterNode.getSelectionContext(originNode));
647  Node sffn = new SlackFileFilterNode(kffn, SlackFileFilterNode.getSelectionContext(originNode));
648 
649  // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
650  //Special case for when File Type Identification has not yet been run and
651  //there are no mime types to populate Files by Mime Type Tree
652  if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
653  EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
654  Node emptyDrfn = new DataResultFilterNode(emptyNode, DirectoryTreeTopComponent.this.em);
655  Node emptyKffn = new KnownFileFilterNode(emptyDrfn, KnownFileFilterNode.getSelectionContext(emptyNode));
656  Node emptySffn = new SlackFileFilterNode(emptyKffn, SlackFileFilterNode.getSelectionContext(originNode));
657  dataResult.setNode(new TableFilterNode(emptySffn, true, "This Node Is Empty")); //NON-NLS
658  } else if (originNode instanceof DisplayableItemNode) {
659  dataResult.setNode(new TableFilterNode(sffn, true, ((DisplayableItemNode) originNode).getItemType()));
660  } else {
661  dataResult.setNode(new TableFilterNode(sffn, true));
662  }
663 
664  String displayName = "";
665  Content content = originNode.getLookup().lookup(Content.class);
666  if (content != null) {
667  try {
668  displayName = content.getUniquePath();
669  } catch (TskCoreException ex) {
670  LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: " + originNode); //NON-NLS
671  }
672  } else if (originNode.getLookup().lookup(String.class) != null) {
673  displayName = originNode.getLookup().lookup(String.class);
674  }
675  dataResult.setPath(displayName);
676  }
677 
678  // set the directory listing to be active
679  if (oldNodes != null && newNodes != null
680  && (oldNodes.length == newNodes.length)) {
681  boolean sameNodes = true;
682  for (int i = 0; i < oldNodes.length; i++) {
683  sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
684  }
685  if (!sameNodes) {
686  dataResult.requestActive();
687  }
688  }
689  } finally {
690  setCursor(null);
691  }
692  }
693  });
694 
695  // update the back and forward list
696  updateHistory(em.getSelectedNodes());
697  }
698 
699  private void updateHistory(Node[] selectedNodes) {
700  if (selectedNodes.length == 0) {
701  return;
702  }
703 
704  Node selectedNode = selectedNodes[0];
705  String selectedNodeName = selectedNode.getName();
706 
707  /*
708  * get the previous entry to make sure we don't duplicate it. Motivation
709  * for this is also that if we used the back button, then we already
710  * added the 'current' node to 'back' and we will detect that and not
711  * reset the forward list.
712  */
713  String[] currentLast = backList.peekLast();
714  String lastNodeName = null;
715  if (currentLast != null) {
716  lastNodeName = currentLast[currentLast.length - 1];
717  }
718 
719  if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
720  //add to the list if the last if not the same as current
721  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
722  backList.addLast(selectedPath); // add the node to the "backList"
723  if (backList.size() > 1) {
724  backButton.setEnabled(true);
725  } else {
726  backButton.setEnabled(false);
727  }
728 
729  forwardList.clear(); // clear the "forwardList"
730  forwardButton.setEnabled(false); // disable the forward Button
731  }
732  }
733 
738  private void resetHistory() {
739  // clear the back and forward list
740  backList.clear();
741  forwardList.clear();
742  backButton.setEnabled(false);
743  forwardButton.setEnabled(false);
744  }
745 
751  public BeanTreeView getTree() {
752  return (BeanTreeView) this.jScrollPane1;
753  }
754 
758  public void refreshContentTreeSafe() {
759  SwingUtilities.invokeLater(new Runnable() {
760  @Override
761  public void run() {
763  }
764  });
765  }
766 
770  private void refreshDataSourceTree() {
771  Node selectedNode = getSelectedNode();
772  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
773 
774  Children rootChildren = em.getRootContext().getChildren();
775  Node dataSourcesFilterNode = rootChildren.findChild(DataSourcesNode.NAME);
776  if (dataSourcesFilterNode == null) {
777  LOGGER.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS
778  return;
779  }
780  DirectoryTreeFilterNode.OriginalNode imagesNodeOrig = dataSourcesFilterNode.getLookup().lookup(DirectoryTreeFilterNode.OriginalNode.class);
781 
782  if (imagesNodeOrig == null) {
783  LOGGER.log(Level.SEVERE, "Cannot find data sources node, won't refresh the content tree"); //NON-NLS
784  return;
785  }
786 
787  Node imagesNode = imagesNodeOrig.getNode();
788 
789  DataSourcesNode.DataSourcesNodeChildren contentRootChildren = (DataSourcesNode.DataSourcesNodeChildren) imagesNode.getChildren();
790  contentRootChildren.refreshContentKeys();
791 
792  //final TreeView tree = getTree();
793  //tree.expandNode(imagesNode);
794  setSelectedNode(selectedPath, DataSourcesNode.NAME);
795 
796  }
797 
804  private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
805  if (previouslySelectedNodePath == null) {
806  return;
807  }
808  SwingUtilities.invokeLater(new Runnable() {
809  @Override
810  public void run() {
811  if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
812  Node selectedNode = null;
813  ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
814  while (null == selectedNode && !selectedNodePath.isEmpty()) {
815  try {
816  selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[0]));
817  } catch (NodeNotFoundException ex) {
818  // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
819  if (selectedNodePath.size() > 1) {
820  selectedNodePath.remove(selectedNodePath.size() - 1);
821  } else {
822  StringBuilder nodePath = new StringBuilder();
823  for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
824  nodePath.append(previouslySelectedNodePath[i]).append("/");
825  }
826  LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
827  break;
828  }
829  }
830  }
831 
832  if (null != selectedNode) {
833  if (rootNodeName != null) {
834  //called from tree auto refresh context
835  //remove last from backlist, because auto select will result in duplication
836  backList.pollLast();
837  }
838  try {
839  em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
840  } catch (PropertyVetoException ex) {
841  LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
842  }
843  }
844  }
845  }
846  });
847  }
848 
849  @Override
850  public TopComponent getTopComponent() {
851  return this;
852  }
853 
854  @Override
855  public boolean hasMenuOpenAction() {
856  return false;
857  }
858 
859  @Override
860  public void viewArtifact(final BlackboardArtifact art) {
861  int typeID = art.getArtifactTypeID();
862  String typeName = art.getArtifactTypeName();
863  Children rootChilds = em.getRootContext().getChildren();
864  Node treeNode = null;
865  Node resultsNode = rootChilds.findChild(ResultsNode.NAME);
866  Children resultsChilds = resultsNode.getChildren();
867  if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
868  Node hashsetRootNode = resultsChilds.findChild(typeName);
869  Children hashsetRootChilds = hashsetRootNode.getChildren();
870  try {
871  String setName = null;
872  List<BlackboardAttribute> attributes = art.getAttributes();
873  for (BlackboardAttribute att : attributes) {
874  int typeId = att.getAttributeType().getTypeID();
875  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
876  setName = att.getValueString();
877  }
878  }
879  treeNode = hashsetRootChilds.findChild(setName);
880  } catch (TskException ex) {
881  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
882  }
883  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
884  Node keywordRootNode = resultsChilds.findChild(typeName);
885  Children keywordRootChilds = keywordRootNode.getChildren();
886  try {
887  String listName = null;
888  String keywordName = null;
889  List<BlackboardAttribute> attributes = art.getAttributes();
890  for (BlackboardAttribute att : attributes) {
891  int typeId = att.getAttributeType().getTypeID();
892  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
893  listName = att.getValueString();
894  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
895  keywordName = att.getValueString();
896  }
897  }
898  Node listNode = keywordRootChilds.findChild(listName);
899  if (listNode == null) {
900  return;
901  }
902  Children listChildren = listNode.getChildren();
903  if (listChildren == null) {
904  return;
905  }
906  treeNode = listChildren.findChild(keywordName);
907  } catch (TskException ex) {
908  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
909  }
910  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
911  || typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
912  Node interestingItemsRootNode = resultsChilds.findChild(typeName);
913  Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
914  try {
915  String setName = null;
916  List<BlackboardAttribute> attributes = art.getAttributes();
917  for (BlackboardAttribute att : attributes) {
918  int typeId = att.getAttributeType().getTypeID();
919  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
920  setName = att.getValueString();
921  }
922  }
923  treeNode = interestingItemsRootChildren.findChild(setName);
924  } catch (TskException ex) {
925  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
926  }
927  } else {
928  Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME);
929  Children extractedChilds = extractedContent.getChildren();
930  if (extractedChilds == null) {
931  return;
932  }
933  treeNode = extractedChilds.findChild(typeName);
934  }
935 
936  if (treeNode == null) {
937  return;
938  }
939 
940  try {
941  em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
942  } catch (PropertyVetoException ex) {
943  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
944  }
945 
946  // Another thread is needed because we have to wait for dataResult to populate
947  EventQueue.invokeLater(new Runnable() {
948  @Override
949  public void run() {
950  Children resultChilds = dataResult.getRootNode().getChildren();
951  Node select = resultChilds.findChild(Long.toString(art.getArtifactID()));
952  if (select != null) {
953  dataResult.requestActive();
954  dataResult.setSelectedNodes(new Node[]{select});
955  fireViewerComplete();
956  }
957  }
958  });
959  }
960 
961  @Override
962  public void viewArtifactContent(BlackboardArtifact art) {
963  new ViewContextAction(
964  NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
965  new BlackboardArtifactNode(art)).actionPerformed(null);
966  }
967 
968  @Override
969  public void addOnFinishedListener(PropertyChangeListener l) {
970  DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
971  }
972 
973  void fireViewerComplete() {
974 
975  try {
976  firePropertyChange(BlackboardResultViewer.FINISHED_DISPLAY_EVT, 0, 1);
977  } catch (Exception e) {
978  LOGGER.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS
979  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr"),
980  NbBundle.getMessage(this.getClass(),
981  "DirectoryTreeTopComponent.moduleErr.msg"),
982  MessageNotifyUtil.MessageType.ERROR);
983  }
984  }
985 }
List< Content > getDataSources()
Definition: Case.java:617
static final String HIDE_KNOWN_FILES_IN_DATA_SOURCES_TREE
static synchronized IngestManager getInstance()
void respondSelection(final Node[] oldNodes, final Node[] newNodes)
void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName)
static final String HIDE_SLACK_FILES_IN_DATA_SOURCES_TREE
void addIngestJobEventListener(final PropertyChangeListener listener)
static synchronized boolean coreComponentsAreActive()
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
static void addChangeListener(PreferenceChangeListener listener)
static synchronized DirectoryTreeTopComponent findInstance()
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:330

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