Autopsy  4.20.0
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 
80  @SuppressWarnings("deprecation")
81  private static final Set<BlackboardArtifact.Type> ARTIFACT_TYPES_OF_INTEREST = ImmutableSet.of(
82  BlackboardArtifact.Type.TSK_HASHSET_HIT,
83  BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT,
84  BlackboardArtifact.Type.TSK_INTERESTING_ITEM
85  );
86 
87  private final PropertyChangeListener ingestEventListener = (evt) -> {
88  Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
89  Long curContentId = AnnotationsContentViewer.this.curContentId;
90 
91  if (curArtifactId == null && curContentId == null) {
92  return;
93  }
94 
95  // if it is a module data event
96  if (IngestManager.IngestModuleEvent.DATA_ADDED.toString().equals(evt.getPropertyName())
97  && evt.getOldValue() instanceof ModuleDataEvent) {
98 
99  ModuleDataEvent moduleDataEvent = (ModuleDataEvent) evt.getOldValue();
100 
101  // if an artifact is relevant, refresh
102  if (ARTIFACT_TYPES_OF_INTEREST.contains(moduleDataEvent.getBlackboardArtifactType())) {
103  for (BlackboardArtifact artifact : moduleDataEvent.getArtifacts()) {
104  if ((curArtifactId != null && artifact.getArtifactID() == curArtifactId)
105  || (curContentId != null && artifact.getObjectID() == curContentId)) {
106  refresh();
107  return;
108  }
109  }
110  }
111  }
112  };
113 
114  private final PropertyChangeListener weakIngestEventListener = WeakListeners.propertyChange(ingestEventListener, null);
115 
116  private final PropertyChangeListener caseEventListener = (evt) -> {
117  Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
118  Long curContentId = AnnotationsContentViewer.this.curContentId;
119 
120  if (curArtifactId == null && curContentId == null) {
121  return;
122  }
123 
124  Pair<Long, Long> artifactContentId = getIdsFromEvent(evt);
125  Long artifactId = artifactContentId.getLeft();
126  Long contentId = artifactContentId.getRight();
127 
128  // if there is a match of content id or artifact id and the event, refresh
129  if ((curArtifactId != null && curArtifactId.equals(artifactId)) || (curContentId != null && curContentId.equals(contentId))) {
130  refresh();
131  }
132  };
133 
134  private final PropertyChangeListener weakCaseEventListener = WeakListeners.propertyChange(caseEventListener, null);
135 
136  private final Object updateLock = new Object();
137 
138  private AnnotationWorker worker = null;
139  private Node node;
140  private Long curArtifactId;
141  private Long curContentId;
142 
147  initComponents();
149  registerListeners();
150  }
151 
155  private void registerListeners() {
156  Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
157  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
158  }
159 
160  @Override
161  protected void finalize() throws Throwable {
162  unregisterListeners();
163  }
164 
168  private void unregisterListeners() {
169  Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
170  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
171  }
172 
173  @Override
174  public void setNode(Node node) {
175  this.node = node;
176  DisplayTskItems displayItems = AnnotationUtils.getDisplayContent(node);
177  this.curArtifactId = displayItems.getArtifact() == null ? null : displayItems.getArtifact().getArtifactID();
178  this.curContentId = displayItems.getContent() == null ? null : displayItems.getContent().getId();
179  updateData(this.node, true);
180  }
181 
191  private static Pair<Long, Long> getIdsFromEvent(PropertyChangeEvent evt) {
192  Case.Events eventType = null;
193  try {
194  eventType = Case.Events.valueOf(evt.getPropertyName());
195  } catch (IllegalArgumentException ex) {
196  logger.log(Level.SEVERE, "Unknown event type: " + evt.getPropertyName(), ex);
197  return Pair.of(null, null);
198  }
199 
200  Long artifactId = null;
201  Long contentId = null;
202 
203  switch (eventType) {
204  case BLACKBOARD_ARTIFACT_TAG_ADDED:
205  if (evt instanceof BlackBoardArtifactTagAddedEvent) {
206  BlackboardArtifact art = ((BlackBoardArtifactTagAddedEvent) evt).getAddedTag().getArtifact();
207  artifactId = art.getArtifactID();
208  contentId = art.getObjectID();
209  }
210  break;
211  case BLACKBOARD_ARTIFACT_TAG_DELETED:
212  if (evt instanceof BlackBoardArtifactTagDeletedEvent) {
213  artifactId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getArtifactID();
214  contentId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
215  }
216  break;
217  case CONTENT_TAG_ADDED:
218  if (evt instanceof ContentTagAddedEvent) {
219  contentId = ((ContentTagAddedEvent) evt).getAddedTag().getContent().getId();
220  }
221  break;
222  case CONTENT_TAG_DELETED:
223  if (evt instanceof ContentTagDeletedEvent) {
224  contentId = ((ContentTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
225  }
226  break;
227  case CR_COMMENT_CHANGED:
228  if (evt instanceof CommentChangedEvent) {
229  long commentObjId = ((CommentChangedEvent) evt).getContentID();
230  artifactId = commentObjId;
231  contentId = commentObjId;
232  }
233  break;
234  default:
235  break;
236  };
237 
238  return Pair.of(artifactId, contentId);
239  }
240 
244  private void refresh() {
245  if (this.isVisible()) {
246  updateData(this.node, false);
247  }
248  }
249 
259  private void updateData(Node node, boolean forceReset) {
260  if (node == null) {
261  return;
262  }
263 
264  if (forceReset) {
265  resetComponent();
266  }
267 
268  synchronized (updateLock) {
269  if (worker != null) {
270  if (forceReset) {
271  worker.cancel(true);
272  worker = null;
273  } else {
274  return;
275  }
276  }
277 
278  worker = new AnnotationWorker(node, forceReset);
279  worker.execute();
280  }
281  }
282 
288  @SuppressWarnings("unchecked")
289  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
290  private void initComponents() {
291 
292  javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
293  textPanel = new javax.swing.JTextPane();
294 
295  setPreferredSize(new java.awt.Dimension(100, 58));
296 
297  textPanel.setEditable(false);
298  textPanel.setName(""); // NOI18N
299  textPanel.setPreferredSize(new java.awt.Dimension(600, 52));
300  scrollPane.setViewportView(textPanel);
301 
302  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
303  this.setLayout(layout);
304  layout.setHorizontalGroup(
305  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
306  .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE)
307  );
308  layout.setVerticalGroup(
309  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
310  .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE)
311  );
312  }// </editor-fold>//GEN-END:initComponents
313 
314  // Variables declaration - do not modify//GEN-BEGIN:variables
315  private javax.swing.JTextPane textPanel;
316  // End of variables declaration//GEN-END:variables
317 
318  @Override
319  public String getTitle() {
320  return Bundle.AnnotationsContentViewer_title();
321  }
322 
323  @Override
324  public String getToolTip() {
325  return Bundle.AnnotationsContentViewer_toolTip();
326  }
327 
328  @Override
330  return new AnnotationsContentViewer();
331  }
332 
333  @Override
334  public boolean isSupported(Node node) {
335  return AnnotationUtils.isSupported(node);
336  }
337 
338  @Override
339  public int isPreferred(Node node) {
340  return ViewerPriority.viewerPriority.LevelOne.getFlag();
341  }
342 
343  @Override
344  public Component getComponent() {
345  return this;
346  }
347 
348  @Override
349  public void resetComponent() {
350  textPanel.setText("");
351 
352  }
353 
358  private class AnnotationWorker extends SwingWorker<String, Void> {
359 
360  private final Node node;
361  private final boolean resetCaretPosition;
362 
370  AnnotationWorker(Node node, boolean resetCaretPosition) {
371  this.node = node;
372  this.resetCaretPosition = resetCaretPosition;
373  }
374 
375  @Override
376  protected String doInBackground() throws Exception {
377  Document doc = AnnotationUtils.buildDocument(node);
378 
379  if (isCancelled()) {
380  return null;
381  }
382 
383  if (doc != null) {
384  return doc.html();
385  } else {
386  return "<span class='" + ContentViewerHtmlStyles.getMessageClassName() + "'>" + Bundle.AnnotationsContentViewer_onEmpty() + "</span>";
387  }
388  }
389 
390  @Override
391  public void done() {
392  if (!isCancelled()) {
393  try {
394  String text = get();
396  textPanel.setText(text);
397 
398  if (resetCaretPosition) {
399  textPanel.setCaretPosition(0);
400  }
401 
402  } catch (InterruptedException | ExecutionException ex) {
403  logger.log(Level.SEVERE, "Failed to get annotation information for node", ex);
404  }
405  }
406 
407  synchronized (updateLock) {
408  if (worker == this) {
409  worker = null;
410  }
411  }
412  }
413 
414  }
415 }
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:704
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:749

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.