Autopsy 4.22.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 */
19package org.sleuthkit.autopsy.contentviewers.annotations;
20
21import com.google.common.collect.ImmutableSet;
22import java.awt.Component;
23import java.beans.PropertyChangeEvent;
24import java.beans.PropertyChangeListener;
25import java.util.EnumSet;
26import java.util.Set;
27import java.util.concurrent.ExecutionException;
28import java.util.logging.Level;
29import javax.swing.SwingWorker;
30import org.apache.commons.lang3.tuple.Pair;
31
32import static org.openide.util.NbBundle.Messages;
33import org.openide.nodes.Node;
34import org.openide.util.lookup.ServiceProvider;
35import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
36import org.sleuthkit.autopsy.coreutils.Logger;
37import org.jsoup.nodes.Document;
38import org.openide.util.WeakListeners;
39import org.sleuthkit.autopsy.casemodule.Case;
40import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
41import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
42import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
43import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
44import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
45import org.sleuthkit.autopsy.contentviewers.annotations.AnnotationUtils.DisplayTskItems;
46import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
47import org.sleuthkit.autopsy.contentviewers.utils.ViewerPriority;
48import org.sleuthkit.autopsy.ingest.IngestManager;
49import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
50import 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})
62public 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
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
151
159
160 @Override
161 protected void finalize() throws Throwable {
163 }
164
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) {
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
332
333 @Override
334 public boolean isSupported(Node 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}
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static synchronized IngestManager getInstance()
void addIngestModuleEventListener(final PropertyChangeListener listener)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.