From ba81377eb0c260ce8bb36c7349b74fc3ca8ce059 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 19 Jun 2015 10:22:18 -0400 Subject: [PATCH] Tag Events - created new Case.Event enum values for BlackBoard/Content tags added/deleted - created new PropertyChangeEvent subclasses for BlackBoard/Content tags added/deleted - replaced ModuleDataEvent hack with new Tag Events - removed [in] from javadocs, other minor cleanup --- .../autopsy/actions/AddTagAction.java | 38 +- .../sleuthkit/autopsy/actions/TagAction.java | 35 +- .../sleuthkit/autopsy/casemodule/Case.java | 113 ++++- .../casemodule/services/TagsManager.java | 392 +++++++++++------- .../org/sleuthkit/autopsy/datamodel/Tags.java | 15 +- .../BlackBoardArtifactTagAddedEvent.java | 33 ++ .../BlackBoardArtifactTagDeletedEvent.java | 32 ++ .../autopsy/events/ContentTagAddedEvent.java | 35 ++ .../events/ContentTagDeletedEvent.java | 34 ++ .../autopsy/events/TagAddedEvent.java | 46 ++ .../autopsy/events/TagDeletedEvent.java | 45 ++ 11 files changed, 580 insertions(+), 238 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagAddedEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagDeletedEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/events/ContentTagAddedEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/events/ContentTagDeletedEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/events/TagAddedEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index b051fa2238..9c552247fd 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-15 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,20 +19,18 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.Collections; import java.util.List; import java.util.logging.Level; import javax.swing.JMenu; import javax.swing.JMenuItem; - import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.coreutils.Logger; /** * An abstract base class for Actions that allow users to tag SleuthKit data @@ -98,12 +96,8 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup { if (null != tagNames && !tagNames.isEmpty()) { for (final TagName tagName : tagNames) { JMenuItem tagNameItem = new JMenuItem(tagName.getDisplayName()); - tagNameItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - addTag(tagName, NO_COMMENT); - refreshDirectoryTree(); - } + tagNameItem.addActionListener((ActionEvent e) -> { + addTag(tagName, NO_COMMENT); }); quickTagMenu.add(tagNameItem); } @@ -120,14 +114,10 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup { // Selecting this item initiates a dialog that can be used to create // or select a tag name and adds a tag with the resulting name. JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag")); - newTagMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - TagName tagName = GetTagNameDialog.doDialog(); - if (tagName != null) { - addTag(tagName, NO_COMMENT); - refreshDirectoryTree(); - } + newTagMenuItem.addActionListener((ActionEvent e) -> { + TagName tagName = GetTagNameDialog.doDialog(); + if (null != tagName) { + addTag(tagName, NO_COMMENT); } }); quickTagMenu.add(newTagMenuItem); @@ -137,14 +127,10 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup { // optional comment and adds a tag with the resulting name. JMenuItem tagAndCommentItem = new JMenuItem( NbBundle.getMessage(this.getClass(), "AddTagAction.tagAndComment")); - tagAndCommentItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog(); - if (null != tagNameAndComment) { - addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment()); - refreshDirectoryTree(); - } + tagAndCommentItem.addActionListener((ActionEvent e) -> { + GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog(); + if (null != tagNameAndComment) { + addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment()); } }); add(tagAndCommentItem); diff --git a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java index a540ce878a..ed53af2c6f 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-15 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,41 +20,26 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.datamodel.BlackboardArtifact; /** * Abstract base class for Actions involving tags. */ - abstract class TagAction extends AbstractAction { +abstract class TagAction extends AbstractAction { + public TagAction(String menuText) { super(menuText); } - + @Override public void actionPerformed(ActionEvent event) { doAction(event); - refreshDirectoryTree(); - } - + } + /** - * Derived classes must implement this Template Method for actionPerformed(). - * @param event ActionEvent object passed to actionPerformed() + * Derived classes must implement this Template Method for + * actionPerformed(). + * + * @param event ActionEvent object passed to actionPerformed() */ abstract protected void doAction(ActionEvent event); - - /** - * Derived classes should call this method any time a tag is created, updated - * or deleted outside of an actionPerformed() call. - */ - @SuppressWarnings("deprecation") - protected void refreshDirectoryTree() { - - /* Note: this is a hack. In an ideal world, TagsManager would fire events so - * that the directory tree would refresh. But, we haven't had a chance to add - * that so, we fire these events and the tree refreshes based on them. - */ - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); //NON-NLS - } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index db1250ebf7..19ec87e3cc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Frame; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.BufferedInputStream; @@ -49,8 +50,19 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.Version; -import org.sleuthkit.datamodel.*; +import org.sleuthkit.autopsy.events.BlackBoardArtifactTagAddedEvent; +import org.sleuthkit.autopsy.events.BlackBoardArtifactTagDeletedEvent; +import org.sleuthkit.autopsy.events.ContentTagAddedEvent; +import org.sleuthkit.autopsy.events.ContentTagDeletedEvent; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.Report; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskException; /** * Stores all information for a given case. Only a single case can currently be @@ -126,7 +138,19 @@ public class Case implements SleuthkitCase.ErrorObserver { * case. The old value supplied by the event object is null and the new * value is a reference to a Report object representing the new report. */ - REPORT_ADDED; + REPORT_ADDED, + /** Property name for the event when a new BlackBoardArtifactTag is + * added. The new value is tag added, the old value is empty */ + BLACKBOARD_ARTIFACT_TAG_ADDED, + /** Property name for the event when a new BlackBoardArtifactTag is + * deleted. The new value is empty, the old value is the deleted tag */ + BLACKBOARD_ARTIFACT_TAG_DELETED, + /** Property name for the event when a new ContentTag is + * added. The new value is tag added, the old value is empty */ + CONTENT_TAG_ADDED, + /** Property name for the event when a new ContentTag is + * deleted. The new value is empty, the old value is the deleted tag */ + CONTENT_TAG_DELETED; }; private String name; @@ -267,12 +291,13 @@ public class Case implements SleuthkitCase.ErrorObserver { /** * Creates a new case (create the XML config file and database) * - * @param caseDir The directory to store case data in. Will be created if it - * doesn't already exist. If it exists, it should have all of the needed sub - * dirs that createCaseDirectory() will create. - * @param caseName the name of case + * @param caseDir The directory to store case data in. Will be created if + * it + * doesn't already exist. If it exists, it should have all of the needed sub + * dirs that createCaseDirectory() will create. + * @param caseName the name of case * @param caseNumber the case number - * @param examiner the examiner for this case + * @param examiner the examiner for this case */ public static void create(String caseDir, String caseName, String caseNumber, String examiner) throws CaseActionException { logger.log(Level.INFO, "Creating new case.\ncaseDir: {0}\ncaseName: {1}", new Object[]{caseDir, caseName}); //NON-NLS @@ -418,7 +443,7 @@ public class Case implements SleuthkitCase.ErrorObserver { * Sends out event and reopens windows if needed. * * @param imgPaths the paths of the image that being added - * @param imgId the ID of the image that being added + * @param imgId the ID of the image that being added * @param timeZone the timeZone of the image where it's added */ @Deprecated @@ -476,6 +501,59 @@ public class Case implements SleuthkitCase.ErrorObserver { CoreComponentControl.openCoreWindows(); } + /** + * Notifies the UI that a new ContentTag has been added. + * + * @param newTag new ContentTag added + */ + public void notifyContentTagAdded(ContentTag newTag) { + notify(new ContentTagAddedEvent(newTag)); + } + + /** + * Notifies the UI that a ContentTag has been deleted. + * + * @param deletedTag ContentTag deleted + */ + public void notifyContentTagDeleted(ContentTag deletedTag) { + notify(new ContentTagDeletedEvent(deletedTag)); + } + + /** + * Notifies the UI that a new BlackboardArtifactTag has been added. + * + * @param newTag new BlackboardArtifactTag added + */ + public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) { + notify(new BlackBoardArtifactTagAddedEvent(newTag)); + } + + /** + * Notifies the UI that a BlackboardArtifactTag has been. + * + * @param deletedTag BlackboardArtifactTag deleted + */ + public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) { + notify(new BlackBoardArtifactTagDeletedEvent(deletedTag)); + } + + /** + * Notifies the UI about a Case level event. + * + * @param propertyChangeEvent the event to distribute + */ + private void notify(final PropertyChangeEvent propertyChangeEvent) { + try { + pcs.firePropertyChange(propertyChangeEvent); + } catch (Exception e) { + logger.log(Level.SEVERE, "Case threw exception", e); //NON-NLS + MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"), + NbBundle.getMessage(this.getClass(), + "Case.changeCase.errListenToCaseUpdates.msg"), + MessageNotifyUtil.MessageType.ERROR); + } + } + /** * @return The Services object for this case. */ @@ -540,9 +618,9 @@ public class Case implements SleuthkitCase.ErrorObserver { * Updates the case name. * * @param oldCaseName the old case name that wants to be updated - * @param oldPath the old path that wants to be updated + * @param oldPath the old path that wants to be updated * @param newCaseName the new case name - * @param newPath the new path + * @param newPath the new path */ void updateCaseName(String oldCaseName, String oldPath, String newCaseName, String newPath) throws CaseActionException { try { @@ -801,6 +879,7 @@ public class Case implements SleuthkitCase.ErrorObserver { * Get the data model Content objects in the root of this case's hierarchy. * * @return a list of the root objects + * * @throws org.sleuthkit.datamodel.TskCoreException */ public List getDataSources() throws TskCoreException { @@ -934,7 +1013,7 @@ public class Case implements SleuthkitCase.ErrorObserver { /** * to create the case directory * - * @param caseDir Path to the case directory (typically base + case name) + * @param caseDir Path to the case directory (typically base + case name) * @param caseName the case name (used only for error messages) * * @throws CaseActionException throw if could not create the case dir @@ -1154,12 +1233,14 @@ public class Case implements SleuthkitCase.ErrorObserver { /** * Adds a report to the case. * - * @param [in] localPath The path of the report file, must be in the case - * directory or one of its subdirectories. - * @param [in] sourceModuleName The name of the module that created the - * report. - * @param [in] reportName The report name, may be empty. + * @param localPath The path of the report file, must be in the case + * directory or one of its subdirectories. + * @param sourceModuleName The name of the module that created the + * report. + * @param reportName The report name, may be empty. + * * @return A Report data transfer object (DTO) for the new row. + * * @throws TskCoreException */ public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index 6be8439029..123238c4e3 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-15 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,9 +24,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; - import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; @@ -37,28 +37,32 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** - * A per case instance of this class functions as an Autopsy service that - * manages the creation, updating, and deletion of tags applied to content and - * blackboard artifacts by users. + * A per case instance of this class functions as an Autopsy service that + * manages the creation, updating, and deletion of tags applied to content and + * blackboard artifacts by users. */ public class TagsManager implements Closeable { + private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS - private final SleuthkitCase tskCase; + private final SleuthkitCase tskCase; private final HashMap uniqueTagNames = new HashMap<>(); private boolean tagNamesInitialized = false; // @@@ This is part of a work around to be removed when database access on the EDT is correctly synchronized. - - // Use this exception and the member hash map to manage uniqueness of hash - // names. This is deemed more proactive and informative than leaving this to - // the UNIQUE constraint on the display_name field of the tag_names table in - // the case database. - public class TagNameAlreadyExistsException extends Exception { - } - + /** - * Package-scope constructor for use of the Services class. An instance of + * Use this exception and the member hash map to manage uniqueness of hash + * names. This is deemed more proactive and informative than leaving this to + * the UNIQUE constraint on the display_name field of the tag_names table in + * the case database. + */ + public static class TagNameAlreadyExistsException extends Exception { + } + + /** + * Package-scope constructor for use of the Services class. An instance of * TagsManager should be created for each case that is opened. - * @param [in] tskCase The SleuthkitCase object for the current case. + * + * @param tskCase The SleuthkitCase object for the current case. */ TagsManager(SleuthkitCase tskCase) { this.tskCase = tskCase; @@ -67,38 +71,44 @@ public class TagsManager implements Closeable { } /** - * Gets a list of all tag names currently available for tagging content or + * Gets a list of all tag names currently available for tagging content or * blackboard artifacts. - * @return A list, possibly empty, of TagName data transfer objects (DTOs). - * @throws TskCoreException + * + * @return A list, possibly empty, of TagName data transfer objects (DTOs). + * + * @throws TskCoreException */ - public synchronized List getAllTagNames() throws TskCoreException { + public synchronized List getAllTagNames() throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + return tskCase.getAllTagNames(); } /** - * Gets a list of all tag names currently used for tagging content or + * Gets a list of all tag names currently used for tagging content or * blackboard artifacts. - * @return A list, possibly empty, of TagName data transfer objects (DTOs). - * @throws TskCoreException + * + * @return A list, possibly empty, of TagName data transfer objects (DTOs). + * + * @throws TskCoreException */ - public synchronized List getTagNamesInUse() throws TskCoreException { + public synchronized List getTagNamesInUse() throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + return tskCase.getTagNamesInUse(); } - + /** * Checks whether a tag name with a given display name exists. - * @param [in] tagDisplayName The display name for which to check. + * + * @param tagDisplayName The display name for which to check. + * * @return True if the tag name exists, false otherwise. */ public synchronized boolean tagNameExists(String tagDisplayName) { @@ -106,15 +116,19 @@ public class TagsManager implements Closeable { if (!tagNamesInitialized) { getExistingTagNames(); } - + return uniqueTagNames.containsKey(tagDisplayName); } - + /** * Adds a new tag name to the current case and to the tags settings. - * @param [in] displayName The display name for the new tag name. - * @return A TagName data transfer object (DTO) representing the new tag name. - * @throws TagNameAlreadyExistsException, TskCoreException + * + * @param displayName The display name for the new tag name. + * + * @return A TagName data transfer object (DTO) representing the new tag + * name. + * + * @throws TagNameAlreadyExistsException, TskCoreException */ public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException { return addTagName(displayName, "", TagName.HTML_COLOR.NONE); @@ -122,10 +136,14 @@ public class TagsManager implements Closeable { /** * Adds a new tag name to the current case and to the tags settings. - * @param [in] displayName The display name for the new tag name. - * @param [in] description The description for the new tag name. - * @return A TagName data transfer object (DTO) representing the new tag name. - * @throws TagNameAlreadyExistsException, TskCoreException + * + * @param displayName The display name for the new tag name. + * @param description The description for the new tag name. + * + * @return A TagName data transfer object (DTO) representing the new tag + * name. + * + * @throws TagNameAlreadyExistsException, TskCoreException */ public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException { return addTagName(displayName, description, TagName.HTML_COLOR.NONE); @@ -133,82 +151,95 @@ public class TagsManager implements Closeable { /** * Adds a new tag name to the current case and to the tags settings. - * @param [in] displayName The display name for the new tag name. - * @param [in] description The description for the new tag name. - * @param [in] color The HTML color to associate with the new tag name. - * @return A TagName data transfer object (DTO) representing the new tag name. - * @throws TagNameAlreadyExistsException, TskCoreException + * + * @param displayName The display name for the new tag name. + * @param description The description for the new tag name. + * @param color The HTML color to associate with the new tag name. + * + * @return A TagName data transfer object (DTO) representing the new tag + * name. + * + * @throws TagNameAlreadyExistsException, TskCoreException */ public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + if (uniqueTagNames.containsKey(displayName)) { throw new TagNameAlreadyExistsException(); } // Add the tag name to the case. - TagName newTagName = tskCase.addTagName(displayName, description, color); + TagName newTagName = tskCase.addTagName(displayName, description, color); // Add the tag name to the tags settings. uniqueTagNames.put(newTagName.getDisplayName(), newTagName); - saveTagNamesToTagsSettings(); + saveTagNamesToTagsSettings(); return newTagName; } - + /** * Tags a content object. - * @param [in] content The content to tag. - * @param [in] tagName The name to use for the tag. - * @return A ContentTag data transfer object (DTO) representing the new tag. - * @throws TskCoreException + * + * @param content The content to tag. + * @param tagName The name to use for the tag. + * + * @return A ContentTag data transfer object (DTO) representing the new tag. + * + * @throws TskCoreException */ public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException { return addContentTag(content, tagName, "", -1, -1); - } - + } + /** * Tags a content object. - * @param [in] content The content to tag. - * @param [in] tagName The name to use for the tag. - * @param [in] comment A comment to store with the tag. - * @return A ContentTag data transfer object (DTO) representing the new tag. - * @throws TskCoreException + * + * @param content The content to tag. + * @param tagName The name to use for the tag. + * @param comment A comment to store with the tag. + * + * @return A ContentTag data transfer object (DTO) representing the new tag. + * + * @throws TskCoreException */ public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException { return addContentTag(content, tagName, comment, -1, -1); - } - + } + /** * Tags a content object or a section of a content object. - * @param [in] content The content to tag. - * @param [in] tagName The name to use for the tag. - * @param [in] comment A comment to store with the tag. - * @param [in] beginByteOffset Designates the beginning of a tagged section. - * @param [in] endByteOffset Designates the end of a tagged section. - * @return A ContentTag data transfer object (DTO) representing the new tag. - * @throws IllegalArgumentException, TskCoreException + * + * @param content The content to tag. + * @param tagName The name to use for the tag. + * @param comment A comment to store with the tag. + * @param beginByteOffset Designates the beginning of a tagged section. + * @param endByteOffset Designates the end of a tagged section. + * + * @return A ContentTag data transfer object (DTO) representing the new tag. + * + * @throws IllegalArgumentException, TskCoreException */ public synchronized ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + if (beginByteOffset >= 0 && endByteOffset >= 1) { if (beginByteOffset > content.getSize() - 1) { throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), - "TagsManager.addContentTag.exception.beginByteOffsetOOR.msg", - beginByteOffset, content.getSize() - 1)); + "TagsManager.addContentTag.exception.beginByteOffsetOOR.msg", + beginByteOffset, content.getSize() - 1)); } if (endByteOffset > content.getSize() - 1) { throw new IllegalArgumentException( NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endByteOffsetOOR.msg", - endByteOffset, content.getSize() - 1)); + endByteOffset, content.getSize() - 1)); } if (endByteOffset < beginByteOffset) { @@ -216,28 +247,34 @@ public class TagsManager implements Closeable { NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endLTbegin.msg")); } } - - return tskCase.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset); + final ContentTag newContentTag = tskCase.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset); + Case.getCurrentCase().notifyContentTagAdded(newContentTag); + return newContentTag; } - + /** * Deletes a content tag. - * @param [in] tag The tag to delete. - * @throws TskCoreException + * + * @param tag The tag to delete. + * + * @throws TskCoreException */ public synchronized void deleteContentTag(ContentTag tag) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + tskCase.deleteContentTag(tag); + Case.getCurrentCase().notifyContentTagDeleted(tag); } /** * Gets all content tags for the current case. + * * @return A list, possibly empty, of content tags. - * @throws TskCoreException + * + * @throws TskCoreException */ public List getAllContentTags() throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. @@ -245,100 +282,126 @@ public class TagsManager implements Closeable { getExistingTagNames(); } - return tskCase.getAllContentTags(); + return tskCase.getAllContentTags(); } - + /** * Gets content tags count by tag name. - * @param [in] tagName The tag name of interest. + * + * @param tagName The tag name of interest. + * * @return A count of the content tags with the specified tag name. - * @throws TskCoreException + * + * @throws TskCoreException */ public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getContentTagsCountByTagName(tagName); + + return tskCase.getContentTagsCountByTagName(tagName); } - + /** * Gets content tags by tag name. - * @param [in] tagName The tag name of interest. - * @return A list, possibly empty, of the content tags with the specified tag name. - * @throws TskCoreException + * + * @param tagName The tag name of interest. + * + * @return A list, possibly empty, of the content tags with the specified + * tag name. + * + * @throws TskCoreException */ public synchronized List getContentTagsByTagName(TagName tagName) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getContentTagsByTagName(tagName); + + return tskCase.getContentTagsByTagName(tagName); } - + /** * Gets content tags count by content. - * @param [in] content The content of interest. - * @return A list, possibly empty, of the tags that have been applied to the artifact. - * @throws TskCoreException + * + * @param content The content of interest. + * + * @return A list, possibly empty, of the tags that have been applied to the + * artifact. + * + * @throws TskCoreException */ public synchronized List getContentTagsByContent(Content content) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getContentTagsByContent(content); + + return tskCase.getContentTagsByContent(content); } - + /** * Tags a blackboard artifact object. - * @param [in] artifact The blackboard artifact to tag. - * @param [in] tagName The name to use for the tag. - * @return A BlackboardArtifactTag data transfer object (DTO) representing the new tag. - * @throws TskCoreException + * + * @param artifact The blackboard artifact to tag. + * @param tagName The name to use for the tag. + * + * @return A BlackboardArtifactTag data transfer object (DTO) representing + * the new tag. + * + * @throws TskCoreException */ public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException { - return addBlackboardArtifactTag(artifact, tagName, ""); + return addBlackboardArtifactTag(artifact, tagName, ""); } - + /** * Tags a blackboard artifact object. - * @param [in] artifact The blackboard artifact to tag. - * @param [in] tagName The name to use for the tag. - * @param [in] comment A comment to store with the tag. - * @return A BlackboardArtifactTag data transfer object (DTO) representing the new tag. - * @throws TskCoreException + * + * @param artifact The blackboard artifact to tag. + * @param tagName The name to use for the tag. + * @param comment A comment to store with the tag. + * + * @return A BlackboardArtifactTag data transfer object (DTO) representing + * the new tag. + * + * @throws TskCoreException */ public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.addBlackboardArtifactTag(artifact, tagName, comment); + + BlackboardArtifactTag addBlackboardArtifactTag = tskCase.addBlackboardArtifactTag(artifact, tagName, comment); + Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(addBlackboardArtifactTag); + return addBlackboardArtifactTag; } /** * Deletes a blackboard artifact tag. - * @param [in] tag The tag to delete. - * @throws TskCoreException + * + * @param tag The tag to delete. + * + * @throws TskCoreException */ public synchronized void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - + tskCase.deleteBlackboardArtifactTag(tag); + Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag); } - + /** * Gets all blackboard artifact tags for the current case. + * * @return A list, possibly empty, of blackboard artifact tags. - * @throws TskCoreException + * + * @throws TskCoreException */ public List getAllBlackboardArtifactTags() throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. @@ -346,100 +409,110 @@ public class TagsManager implements Closeable { getExistingTagNames(); } - return tskCase.getAllBlackboardArtifactTags(); + return tskCase.getAllBlackboardArtifactTags(); } - + /** * Gets blackboard artifact tags count by tag name. - * @param [in] tagName The tag name of interest. - * @return A count of the blackboard artifact tags with the specified tag name. - * @throws TskCoreException + * + * @param tagName The tag name of interest. + * + * @return A count of the blackboard artifact tags with the specified tag + * name. + * + * @throws TskCoreException */ public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getBlackboardArtifactTagsCountByTagName(tagName); + + return tskCase.getBlackboardArtifactTagsCountByTagName(tagName); } - + /** * Gets blackboard artifact tags by tag name. - * @param [in] tagName The tag name of interest. - * @return A list, possibly empty, of the blackboard artifact tags with the specified tag name. - * @throws TskCoreException + * + * @param tagName The tag name of interest. + * + * @return A list, possibly empty, of the blackboard artifact tags with the + * specified tag name. + * + * @throws TskCoreException */ - public synchronized List getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException { + public synchronized List getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getBlackboardArtifactTagsByTagName(tagName); + + return tskCase.getBlackboardArtifactTagsByTagName(tagName); } /** * Gets blackboard artifact tags for a particular blackboard artifact. - * @param [in] artifact The blackboard artifact of interest. - * @return A list, possibly empty, of the tags that have been applied to the artifact. - * @throws TskCoreException + * + * @param artifact The blackboard artifact of interest. + * + * @return A list, possibly empty, of the tags that have been applied to the + * artifact. + * + * @throws TskCoreException */ - public synchronized List getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException { + public synchronized List getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException { // @@@ This is a work around to be removed when database access on the EDT is correctly synchronized. if (!tagNamesInitialized) { getExistingTagNames(); } - - return tskCase.getBlackboardArtifactTagsByArtifact(artifact); + + return tskCase.getBlackboardArtifactTagsByArtifact(artifact); } - + @Override - public void close() throws IOException { - saveTagNamesToTagsSettings(); - } - + public void close() throws IOException { + saveTagNamesToTagsSettings(); + } + private void getExistingTagNames() { getTagNamesFromCurrentCase(); getTagNamesFromTagsSettings(); getPredefinedTagNames(); - saveTagNamesToTagsSettings(); + saveTagNamesToTagsSettings(); tagNamesInitialized = true; // @@@ This is part of a work around to be removed when database access on the EDT is correctly synchronized. } - + private void getTagNamesFromCurrentCase() { try { List currentTagNames = tskCase.getAllTagNames(); for (TagName tagName : currentTagNames) { uniqueTagNames.put(tagName.getDisplayName(), tagName); } - } - catch (TskCoreException ex) { + } catch (TskCoreException ex) { Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex); //NON-NLS - } + } } - + private void getTagNamesFromTagsSettings() { String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - if (null != setting && !setting.isEmpty()) { + if (null != setting && !setting.isEmpty()) { // Read the tag name setting and break it into tag name tuples. - List tagNameTuples = Arrays.asList(setting.split(";")); - + List tagNameTuples = Arrays.asList(setting.split(";")); + // Parse each tuple and add the tag names to the current case, one // at a time to gracefully discard any duplicates or corrupt tuples. - for (String tagNameTuple : tagNameTuples) { + for (String tagNameTuple : tagNameTuples) { String[] tagNameAttributes = tagNameTuple.split(","); - if (!uniqueTagNames.containsKey(tagNameAttributes[0])) { + if (!uniqueTagNames.containsKey(tagNameAttributes[0])) { try { TagName tagName = tskCase.addTagName(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.getColorByName(tagNameAttributes[2])); uniqueTagNames.put(tagName.getDisplayName(), tagName); - } - catch (TskCoreException ex) { + } catch (TskCoreException ex) { Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add saved tag name " + tagNameAttributes[0], ex); //NON-NLS } } } - } + } } private void getPredefinedTagNames() { @@ -448,13 +521,12 @@ public class TagsManager implements Closeable { TagName tagName = tskCase.addTagName( NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), "", TagName.HTML_COLOR.NONE); uniqueTagNames.put(tagName.getDisplayName(), tagName); - } - catch (TskCoreException ex) { + } catch (TskCoreException ex) { Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add predefined 'Bookmark' tag name", ex); //NON-NLS } } - } - + } + private void saveTagNamesToTagsSettings() { if (!uniqueTagNames.isEmpty()) { StringBuilder setting = new StringBuilder(); @@ -466,7 +538,7 @@ public class TagsManager implements Closeable { setting.append(tagName.getDescription()).append(","); setting.append(tagName.getColor().name()); } - ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); + ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); } - } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index cc7958355e..5dccc5252d 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -35,8 +35,6 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TagName; @@ -119,19 +117,14 @@ public class Tags implements AutopsyVisitableItem { private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override - @SuppressWarnings("deprecation") public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { - /* Note: this is a hack. In an ideal world, TagsManager - * would fire events so that the directory tree would - * refresh. But, we haven't had a chance to add that so, we - * fire these events and the tree refreshes based on them. */ - if ((((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT) - || ((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE) { + if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString()) + || eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString()) + || eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString()) + || eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { refresh(true); tagResults.update(); - } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { refresh(true); tagResults.update(); diff --git a/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagAddedEvent.java new file mode 100644 index 0000000000..d50bd2237b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagAddedEvent.java @@ -0,0 +1,33 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.BlackboardArtifactTag; + +/** + * + */ +public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent { + + public BlackBoardArtifactTagAddedEvent(BlackboardArtifactTag newTag) { + super(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), newTag + ); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagDeletedEvent.java new file mode 100644 index 0000000000..b2901ac5dc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/BlackBoardArtifactTagDeletedEvent.java @@ -0,0 +1,32 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.BlackboardArtifactTag; + +/** + * + */ +public class BlackBoardArtifactTagDeletedEvent extends TagDeletedEvent { + + public BlackBoardArtifactTagDeletedEvent(BlackboardArtifactTag oldValue) { + super(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString(), oldValue); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/events/ContentTagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/events/ContentTagAddedEvent.java new file mode 100644 index 0000000000..ffd196a4c7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/ContentTagAddedEvent.java @@ -0,0 +1,35 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.ContentTag; + +/** + * An event that is fired when a ContentTag is added. + */ +@Immutable +public class ContentTagAddedEvent extends TagAddedEvent { + + public ContentTagAddedEvent(ContentTag newTag) { + super(Case.Events.CONTENT_TAG_ADDED.toString(), newTag); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/events/ContentTagDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/events/ContentTagDeletedEvent.java new file mode 100644 index 0000000000..6c6d11745f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/ContentTagDeletedEvent.java @@ -0,0 +1,34 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.ContentTag; + +/** + * An event that is fired when a ContentTag is deleted. + */ +@Immutable +public class ContentTagDeletedEvent extends TagDeletedEvent { + + public ContentTagDeletedEvent(ContentTag deletedTag) { + super(Case.Events.CONTENT_TAG_DELETED.toString(), deletedTag); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/events/TagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/events/TagAddedEvent.java new file mode 100644 index 0000000000..b9acb256bb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/TagAddedEvent.java @@ -0,0 +1,46 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import java.beans.PropertyChangeEvent; +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Tag; + +/** + * Base Class for events that are fired when a Tag is added + */ +@Immutable +abstract class TagAddedEvent extends PropertyChangeEvent { + + protected TagAddedEvent(String propertyName, T newValue) { + super(Case.class, propertyName, null, newValue); + } + + /** + * get the Tag that was added + * + * @return the tTag + */ + @SuppressWarnings("unchecked") + public T getAddedTag() { + return (T) getNewValue(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java new file mode 100644 index 0000000000..7d0cfca18e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java @@ -0,0 +1,45 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.events; + +import java.beans.PropertyChangeEvent; +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Tag; + +/** + * Base Class for events that are fired when a Tag is deleted + */ +@Immutable +abstract class TagDeletedEvent extends PropertyChangeEvent { + + protected TagDeletedEvent(String propertyName, T oldValue) { + super(Case.class, propertyName, oldValue, null); + } + + /** + * get the Tag that was deleted + * + * @return the Tag + */ + @SuppressWarnings("unchecked") + public T getDeletedTag() { + return (T) getOldValue(); + } +}