Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
AnnotationsContentViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2018-2021 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.contentviewers.annotations;
20 
21 import com.google.common.collect.ImmutableSet;
22 import java.awt.Component;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.util.EnumSet;
26 import java.util.Set;
27 import java.util.concurrent.ExecutionException;
28 import java.util.logging.Level;
29 import javax.swing.SwingWorker;
30 import org.apache.commons.lang3.tuple.Pair;
31 
32 import static org.openide.util.NbBundle.Messages;
33 import org.openide.nodes.Node;
34 import org.openide.util.lookup.ServiceProvider;
37 import org.jsoup.nodes.Document;
38 import org.openide.util.WeakListeners;
50 import org.sleuthkit.datamodel.BlackboardArtifact;
51 
55 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
56 @ServiceProvider(service = DataContentViewer.class, position = 9)
57 @Messages({
58  "AnnotationsContentViewer.title=Annotations",
59  "AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.",
60  "AnnotationsContentViewer.onEmpty=No annotations were found for this particular item."
61 })
62 public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer {
63 
64  private static final long serialVersionUID = 1L;
65  private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName());
66 
67  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
73 
74  private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
75 
76  private static final Set<BlackboardArtifact.Type> ARTIFACT_TYPES_OF_INTEREST = ImmutableSet.of(
77  BlackboardArtifact.Type.TSK_HASHSET_HIT,
78  BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT
79  );
80 
81  private final PropertyChangeListener ingestEventListener = (evt) -> {
82  Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
83  Long curContentId = AnnotationsContentViewer.this.curContentId;
84 
85  if (curArtifactId == null && curContentId == null) {
86  return;
87  }
88 
89  // if it is a module data event
90  if (IngestManager.IngestModuleEvent.DATA_ADDED.toString().equals(evt.getPropertyName())
91  && evt.getOldValue() instanceof ModuleDataEvent) {
92 
93  ModuleDataEvent moduleDataEvent = (ModuleDataEvent) evt.getOldValue();
94 
95  // if an artifact is relevant, refresh
96  if (ARTIFACT_TYPES_OF_INTEREST.contains(moduleDataEvent.getBlackboardArtifactType())) {
97  for (BlackboardArtifact artifact : moduleDataEvent.getArtifacts()) {
98  if ((curArtifactId != null && artifact.getArtifactID() == curArtifactId)
99  || (curContentId != null && artifact.getObjectID() == curContentId)) {
100  refresh();
101  return;
102  }
103  }
104  }
105  }
106  };
107 
108  private final PropertyChangeListener weakIngestEventListener = WeakListeners.propertyChange(ingestEventListener, null);
109 
110  private final PropertyChangeListener caseEventListener = (evt) -> {
111  Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
112  Long curContentId = AnnotationsContentViewer.this.curContentId;
113 
114  if (curArtifactId == null && curContentId == null) {
115  return;
116  }
117 
118  Pair<Long, Long> artifactContentId = getIdsFromEvent(evt);
119  Long artifactId = artifactContentId.getLeft();
120  Long contentId = artifactContentId.getRight();
121 
122  // if there is a match of content id or artifact id and the event, refresh
123  if ((curArtifactId != null && curArtifactId.equals(artifactId)) || (curContentId != null && curContentId.equals(contentId))) {
124  refresh();
125  }
126  };
127 
128  private final PropertyChangeListener weakCaseEventListener = WeakListeners.propertyChange(caseEventListener, null);
129 
130  private final Object updateLock = new Object();
131 
132  private AnnotationWorker worker = null;
133  private Node node;
134  private Long curArtifactId;
135  private Long curContentId;
136 
141  initComponents();
143  registerListeners();
144  }
145 
149  private void registerListeners() {
150  Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
151  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
152  }
153 
154  @Override
155  protected void finalize() throws Throwable {
156  unregisterListeners();
157  }
158 
162  private void unregisterListeners() {
163  Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
164  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
165  }
166 
167  @Override
168  public void setNode(Node node) {
169  this.node = node;
170  DisplayTskItems displayItems = AnnotationUtils.getDisplayContent(node);
171  this.curArtifactId = displayItems.getArtifact() == null ? null : displayItems.getArtifact().getArtifactID();
172  this.curContentId = displayItems.getContent() == null ? null : displayItems.getContent().getId();
173  updateData(this.node, true);
174  }
175 
185  private static Pair<Long, Long> getIdsFromEvent(PropertyChangeEvent evt) {
186  Case.Events eventType = null;
187  try {
188  eventType = Case.Events.valueOf(evt.getPropertyName());
189  } catch (IllegalArgumentException ex) {
190  logger.log(Level.SEVERE, "Unknown event type: " + evt.getPropertyName(), ex);
191  return Pair.of(null, null);
192  }
193 
194  Long artifactId = null;
195  Long contentId = null;
196 
197  switch (eventType) {
198  case BLACKBOARD_ARTIFACT_TAG_ADDED:
199  if (evt instanceof BlackBoardArtifactTagAddedEvent) {
200  BlackboardArtifact art = ((BlackBoardArtifactTagAddedEvent) evt).getAddedTag().getArtifact();
201  artifactId = art.getArtifactID();
202  contentId = art.getObjectID();
203  }
204  break;
205  case BLACKBOARD_ARTIFACT_TAG_DELETED:
206  if (evt instanceof BlackBoardArtifactTagDeletedEvent) {
207  artifactId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getArtifactID();
208  contentId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
209  }
210  break;
211  case CONTENT_TAG_ADDED:
212  if (evt instanceof ContentTagAddedEvent) {
213  contentId = ((ContentTagAddedEvent) evt).getAddedTag().getContent().getId();
214  }
215  break;
216  case CONTENT_TAG_DELETED:
217  if (evt instanceof ContentTagDeletedEvent) {
218  contentId = ((ContentTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
219  }
220  break;
221  case CR_COMMENT_CHANGED:
222  if (evt instanceof CommentChangedEvent) {
223  long commentObjId = ((CommentChangedEvent) evt).getContentID();
224  artifactId = commentObjId;
225  contentId = commentObjId;
226  }
227  break;
228  default:
229  break;
230  };
231 
232  return Pair.of(artifactId, contentId);
233  }
234 
238  private void refresh() {
239  if (this.isVisible()) {
240  updateData(this.node, false);
241  }
242  }
243 
253  private void updateData(Node node, boolean forceReset) {
254  if (node == null) {
255  return;
256  }
257 
258  if (forceReset) {
259  resetComponent();
260  }
261 
262  synchronized (updateLock) {
263  if (worker != null) {
264  if (forceReset) {
265  worker.cancel(true);
266  worker = null;
267  } else {
268  return;
269  }
270  }
271 
272  worker = new AnnotationWorker(node, forceReset);
273  worker.execute();
274  }
275  }
276 
282  @SuppressWarnings("unchecked")
283  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
284  private void initComponents() {
285 
286  javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
287  textPanel = new javax.swing.JTextPane();
288 
289  setPreferredSize(new java.awt.Dimension(100, 58));
290 
291  textPanel.setEditable(false);
292  textPanel.setName(""); // NOI18N
293  textPanel.setPreferredSize(new java.awt.Dimension(600, 52));
294  scrollPane.setViewportView(textPanel);
295 
296  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
297  this.setLayout(layout);
298  layout.setHorizontalGroup(
299  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
300  .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE)
301  );
302  layout.setVerticalGroup(
303  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
304  .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE)
305  );
306  }// </editor-fold>//GEN-END:initComponents
307 
308  // Variables declaration - do not modify//GEN-BEGIN:variables
309  private javax.swing.JTextPane textPanel;
310  // End of variables declaration//GEN-END:variables
311 
312  @Override
313  public String getTitle() {
314  return Bundle.AnnotationsContentViewer_title();
315  }
316 
317  @Override
318  public String getToolTip() {
319  return Bundle.AnnotationsContentViewer_toolTip();
320  }
321 
322  @Override
324  return new AnnotationsContentViewer();
325  }
326 
327  @Override
328  public boolean isSupported(Node node) {
329  return AnnotationUtils.isSupported(node);
330  }
331 
332  @Override
333  public int isPreferred(Node node) {
334  return ViewerPriority.viewerPriority.LevelOne.getFlag();
335  }
336 
337  @Override
338  public Component getComponent() {
339  return this;
340  }
341 
342  @Override
343  public void resetComponent() {
344  textPanel.setText("");
345 
346  }
347 
352  private class AnnotationWorker extends SwingWorker<String, Void> {
353 
354  private final Node node;
355  private final boolean resetCaretPosition;
356 
364  AnnotationWorker(Node node, boolean resetCaretPosition) {
365  this.node = node;
366  this.resetCaretPosition = resetCaretPosition;
367  }
368 
369  @Override
370  protected String doInBackground() throws Exception {
371  Document doc = AnnotationUtils.buildDocument(node);
372 
373  if (isCancelled()) {
374  return null;
375  }
376 
377  if (doc != null) {
378  return doc.html();
379  } else {
380  return "<span class='" + ContentViewerHtmlStyles.getMessageClassName() + "'>" + Bundle.AnnotationsContentViewer_onEmpty() + "</span>";
381  }
382  }
383 
384  @Override
385  public void done() {
386  if (!isCancelled()) {
387  try {
388  String text = get();
390  textPanel.setText(text);
391 
392  if (resetCaretPosition) {
393  textPanel.setCaretPosition(0);
394  }
395 
396  } catch (InterruptedException | ExecutionException ex) {
397  logger.log(Level.SEVERE, "Failed to get annotation information for node", ex);
398  }
399  }
400 
401  synchronized (updateLock) {
402  if (worker == this) {
403  worker = null;
404  }
405  }
406  }
407 
408  }
409 }
Collection< BlackboardArtifact > getArtifacts()
BlackboardArtifact.Type getBlackboardArtifactType()
static synchronized IngestManager getInstance()
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:711
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:756

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.