diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index 3d394e9215..26288a54a2 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-15 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; */ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { + private static final long serialVersionUID = 1L; private static final String NO_COMMENT = ""; AddTagAction(String menuText) { @@ -82,6 +83,8 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { // to be reworked. private class TagMenu extends JMenu { + private static final long serialVersionUID = 1L; + TagMenu() { super(getActionDisplayName()); @@ -89,10 +92,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); Map tagNamesMap = null; try { - tagNamesMap = new TreeMap<>(); - tagNamesMap.putAll(tagsManager.getUserTagNamesMap()); - tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap()); - tagNamesMap.putAll(tagsManager.getTagNamesInUseMap()); + tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); } catch (TskCoreException ex) { Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java index fa2b1b1593..73290602e9 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java +++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java @@ -91,13 +91,18 @@ public class GetTagNameAndCommentDialog extends JDialog { * dialog. */ public static TagNameAndComment doDialog(Window owner) { - return new GetTagNameAndCommentDialog(owner).tagNameAndComment; + GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner); + dialog.display(); + return dialog.tagNameAndComment; } private GetTagNameAndCommentDialog(Window owner) { super(owner, NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.createTag"), ModalityType.APPLICATION_MODAL); + } + + private void display() { initComponents(); // Set up the dialog to close when Esc is pressed. @@ -106,6 +111,7 @@ public class GetTagNameAndCommentDialog extends JDialog { inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName); ActionMap actionMap = getRootPane().getActionMap(); actionMap.put(cancelName, new AbstractAction() { + private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { dispose(); @@ -118,9 +124,7 @@ public class GetTagNameAndCommentDialog extends JDialog { // not exist in the database). TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); try { - tagNamesMap.putAll(tagsManager.getUserTagNamesMap()); - tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap()); - tagNamesMap.putAll(tagsManager.getTagNamesInUseMap()); + tagNamesMap.putAll(tagsManager.getDisplayNamesToTagNamesMap()); } catch (TskCoreException ex) { Logger.getLogger(GetTagNameAndCommentDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } @@ -133,10 +137,10 @@ public class GetTagNameAndCommentDialog extends JDialog { } // Center and show the dialog box. - this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - setVisible(true); + this.setLocationRelativeTo(this.getOwner()); + setVisible(true); } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java index 5386af785a..3d1057a041 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java +++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,6 +45,7 @@ import org.sleuthkit.datamodel.TskCoreException; public class GetTagNameDialog extends JDialog { + private static final long serialVersionUID = 1L; private static final String TAG_ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS private final Map tagNamesMap = new TreeMap<>(); private TagName tagName = null; @@ -71,14 +72,19 @@ public class GetTagNameDialog extends JDialog { * @return a TagName instance selected by the user, or null if the user * canceled the dialog. */ - public static TagName doDialog(final Window owner) { - return new GetTagNameDialog(owner).tagName; + public static TagName doDialog(Window owner) { + GetTagNameDialog dialog = new GetTagNameDialog(owner); + dialog.display(); + return dialog.tagName; } - private GetTagNameDialog(final Window owner) { - super(owner, + private GetTagNameDialog(Window owner) { + super(owner, NbBundle.getMessage(GetTagNameDialog.class, "GetTagNameDialog.createTag"), ModalityType.APPLICATION_MODAL); + } + + private void display() { setIconImage(ImageUtilities.loadImage(TAG_ICON_PATH)); initComponents(); @@ -88,6 +94,8 @@ public class GetTagNameDialog extends JDialog { inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName); ActionMap actionMap = getRootPane().getActionMap(); actionMap.put(cancelName, new AbstractAction() { + private static final long serialVersionUID = 1L; + @Override public void actionPerformed(ActionEvent e) { cancelButtonActionPerformed(e); @@ -98,27 +106,26 @@ public class GetTagNameDialog extends JDialog { // case the user chooses an existing tag name from the tag names table. TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); try { - tagNamesMap.putAll(tagsManager.getUserTagNamesMap()); - tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap()); - tagNamesMap.putAll(tagsManager.getTagNamesInUseMap()); + tagNamesMap.putAll(tagsManager.getDisplayNamesToTagNamesMap()); } catch (TskCoreException ex) { Logger.getLogger(GetTagNameDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } // Populate the tag names table. - tagsTable.setModel(new TagsTableModel(new ArrayList(tagNamesMap.keySet()))); + tagsTable.setModel(new TagsTableModel(new ArrayList<>(tagNamesMap.keySet()))); tagsTable.setTableHeader(null); tagsTable.setCellSelectionEnabled(false); tagsTable.setFocusable(false); tagsTable.setRowHeight(tagsTable.getRowHeight() + 5); // Center and show the dialog box. - this.setLocationRelativeTo(owner); - setVisible(true); + this.setLocationRelativeTo(this.getOwner()); + setVisible(true); } - + private class TagsTableModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; private final ArrayList tagDisplayNames = new ArrayList<>(); TagsTableModel(List tagDisplayNames) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties index ed80e8696e..229d26af58 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties @@ -1,20 +1,6 @@ OptionsCategory_Name_TagNamesOptions=Tags OptionsCategory_TagNames=TagNames -TagsManager.addContentTag.exception.beginByteOffsetOOR.msg=beginByteOffset \= {0} out of content size range (0 - {1}) -TagsManager.addContentTag.exception.endByteOffsetOOR.msg=endByteOffset \= {0} out of content size range (0 - {1}) -TagsManager.addContentTag.exception.endLTbegin.msg=endByteOffset < beginByteOffset -TagsManager.predefTagNames.bookmark.text=Bookmark -TagsManager.addContentTag.noCaseWarning=Failed to publish new content tag event. There is no case open. -TagsManager.deleteContentTag.noCaseWarning=Failed to publish content tag deleted event. There is no case open. -TagsManager.addBlackboardArtifactTag.noCaseWarning=Failed to publish new blackboard artifact tag event. There is no case open. -TagsManager.deleteBlackboardArtifactTag.noCaseWarning=Failed to publish blackboard artifact tag deleted event. There is no case open. Blackboard.unableToIndexArtifact.error.msg=Unable to index blackboard artifact {0} -TagNamesSettingsPanel.deleteTagNameButton.text=Delete Tag Name -TagNamesSettingsPanel.tagNamesListLabel.text=Tag names: -NewUserTagNameDialog.tagNameTextField.text= -NewUserTagNameDialog.newTagNameLabel.text=New Tag Name: -NewUserTagNameDialog.okButton.text=OK -NewUserTagNameDialog.cancelButton.text=Cancel NewUserTagNameDialog.title.text=New Tag Name NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols\: \\ \: * ? " < > | , ; NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.title=Invalid character in tag name @@ -22,5 +8,11 @@ TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message=The tag name alre TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title=Tag name already exists NewUserTagNameDialog.JOptionPane.tagNameEmpty.message=The tag name cannot be empty NewUserTagNameDialog.JOptionPane.tagNameEmpty.title=Empty tag name -TagNamesSettingsPanel.panelDescriptionLabel.text=Autopsy keeps a list of the tag names you have created in the past. Add more or delete them here. -TagNamesSettingsPanel.newTagNameButton.text=New Tag Name +TagOptionsPanel.tagTypesListLabel.text=Tag Names: +TagOptionsPanel.panelDescriptionLabel.text=Autopsy keeps a list of the tag names you have created in the past. Add more or delete them here. +NewTagNameDialog.okButton.text=OK +NewTagNameDialog.cancelButton.text=Cancel +NewTagNameDialog.tagNameTextField.text= +NewTagNameDialog.newTagNameLabel.text=New Tag Name: +TagOptionsPanel.deleteTagNameButton.text=Delete Tag Name +TagOptionsPanel.newTagNameButton.text=New Tag Name diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties index ca8ed3431a..a27e3ca586 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties @@ -1,9 +1,2 @@ -TagsManager.addContentTag.exception.beginByteOffsetOOR.msg=beginByteOffset \= {0} \u30b3\u30f3\u30c6\u30f3\u30c4\u30b5\u30a4\u30ba\u7bc4\u56f2(0 - {1})\u306e\u5916\u3067\u3059 -TagsManager.addContentTag.exception.endByteOffsetOOR.msg=endByteOffset \= {0} \u30b3\u30f3\u30c6\u30f3\u30c4\u30b5\u30a4\u30ba\u7bc4\u56f2(0 - {1})\u306e\u5916\u3067\u3059 -TagsManager.addContentTag.exception.endLTbegin.msg=endByteOffset < beginByteOffset TagsManager.predefTagNames.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af -TagsManager.addContentTag.noCaseWarning=\u65b0\u3057\u3044\u30bf\u30b0\u30a4\u30d9\u30f3\u30c8\u3092\u30d1\u30d6\u30ea\u30c3\u30b7\u30e5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -TagsManager.deleteContentTag.noCaseWarning=\u524a\u9664\u3055\u308c\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30b0\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u30d1\u30d6\u30ea\u30c3\u30b7\u30e5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -TagsManager.addBlackboardArtifactTag.noCaseWarning=\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30b0\u306e\u65b0\u3057\u3044blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30d1\u30d6\u30ea\u30c3\u30b7\u30e5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -TagsManager.deleteBlackboardArtifactTag.noCaseWarning=\u524a\u9664\u3055\u308c\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30b0\u306e\u65b0\u3057\u3044blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30d1\u30d6\u30ea\u30c3\u30b7\u30e5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 Blackboard.unableToIndexArtifact.error.msg=blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8{0}\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form similarity index 90% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.form rename to Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form index 26ab153c27..b6400b7c31 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form @@ -63,21 +63,21 @@ - + - + - + @@ -87,7 +87,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.java similarity index 87% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.java rename to Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.java index bd04678ba9..fbb7594c44 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewUserTagNameDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.java @@ -29,8 +29,9 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.openide.util.NbBundle; -class NewUserTagNameDialog extends javax.swing.JDialog { +final class NewTagNameDialog extends javax.swing.JDialog { + private static final long serialVersionUID = 1L; private String userTagDisplayName; private BUTTON_PRESSED result; @@ -41,9 +42,9 @@ class NewUserTagNameDialog extends javax.swing.JDialog { /** * Creates a new NewUserTagNameDialog dialog. */ - NewUserTagNameDialog() { - super(new JFrame(NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.title.text")), - NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.title.text"), true); + NewTagNameDialog() { + super(new JFrame(NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.title.text")), + NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.title.text"), true); initComponents(); this.display(); } @@ -111,15 +112,15 @@ class NewUserTagNameDialog extends javax.swing.JDialog { String newTagDisplayName = tagNameTextField.getText().trim(); if (newTagDisplayName.isEmpty()) { JOptionPane.showMessageDialog(null, - NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameEmpty.message"), - NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameEmpty.title"), + NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameEmpty.message"), + NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameEmpty.title"), JOptionPane.ERROR_MESSAGE); return; } if (TagsManager.containsIllegalCharacters(newTagDisplayName)) { JOptionPane.showMessageDialog(null, - NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.message"), - NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.title"), + NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.message"), + NbBundle.getMessage(NewTagNameDialog.class, "NewUserTagNameDialog.JOptionPane.tagNameIllegalCharacters.title"), JOptionPane.ERROR_MESSAGE); return; } @@ -175,18 +176,18 @@ class NewUserTagNameDialog extends javax.swing.JDialog { setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - org.openide.awt.Mnemonics.setLocalizedText(newTagNameLabel, org.openide.util.NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.newTagNameLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(newTagNameLabel, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.newTagNameLabel.text")); // NOI18N - tagNameTextField.setText(org.openide.util.NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.tagNameTextField.text")); // NOI18N + tagNameTextField.setText(org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.tagNameTextField.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.cancelButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.cancelButton.text")); // NOI18N cancelButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(NewUserTagNameDialog.class, "NewUserTagNameDialog.okButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.okButton.text")); // NOI18N okButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { okButtonActionPerformed(evt); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java new file mode 100644 index 0000000000..101d68fa4c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java @@ -0,0 +1,180 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.casemodule.services; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.datamodel.TagName; + +/** + * A tag name definition consisting of a display name, description and color. + */ +@Immutable +final class TagNameDefiniton implements Comparable { + + private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS + private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS + private final String displayName; + private final String description; + private final TagName.HTML_COLOR color; + + /** + * Constructs a tag name definition consisting of a display name, + * description and color. + * + * @param displayName The display name for the tag name. + * @param description The description for the tag name. + * @param color The color for the tag name. + */ + TagNameDefiniton(String displayName, String description, TagName.HTML_COLOR color) { + this.displayName = displayName; + this.description = description; + this.color = color; + } + + /** + * Gets the display name for the tag name. + * + * @return The display name. + */ + String getDisplayName() { + return displayName; + } + + /** + * Gets the description for the tag name. + * + * @return The description. + */ + String getDescription() { + return description; + } + + /** + * Gets the color for the tag name. + * + * @return The color. + */ + TagName.HTML_COLOR getColor() { + return color; + } + + /** + * Compares this tag name definition with the specified tag name definition + * for order. + * + * @param other The tag name definition to which to compare this tag name + * definition. + * + * @return Negative integer, zero, or a positive integer to indicate that + * this tag name definition is less than, equal to, or greater than + * the specified tag name definition. + */ + @Override + public int compareTo(TagNameDefiniton other) { + return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase()); + } + + /** + * Returns a hash code value for this tag name definition. + * + * @return The has code. + */ + @Override + public int hashCode() { + int hash = 7; + hash = 83 * hash + Objects.hashCode(this.displayName); + return hash; + } + + /** + * Indicates whether some other object is "equal to" this tag name + * definition. + * + * @param obj The object to test for equality. + * + * @return True or false. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TagNameDefiniton)) { + return false; + } + TagNameDefiniton thatTagName = (TagNameDefiniton) obj; + return this.getDisplayName().equals(thatTagName.getDisplayName()); + } + + /** + * A string representation of this tag name definition. + * + * @return The display name of the tag type. + */ + @Override + public String toString() { + return displayName; + } + + /** + * @return A string representation of the tag name definition in the format + * that is used by the tags settings file. + */ + private String toSettingsFormat() { + return displayName + "," + description + "," + color.name(); + } + + /** + * Gets tag name definitions from the tag settings file. + * + * @return A set of tag name definition objects. + */ + static synchronized Set getTagNameDefinitions() { + Set tagNames = new HashSet<>(); + String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); + if (null != setting && !setting.isEmpty()) { + List tagNameTuples = Arrays.asList(setting.split(";")); + for (String tagNameTuple : tagNameTuples) { + String[] tagNameAttributes = tagNameTuple.split(","); + tagNames.add(new TagNameDefiniton(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.valueOf(tagNameAttributes[2]))); + } + } + return tagNames; + } + + /** + * Sets the tag name definitions in the tag settings file. + * + * @param tagNames A set of tag name definition objects. + */ + static synchronized void setTagNameDefinitions(Set tagNames) { + StringBuilder setting = new StringBuilder(); + for (TagNameDefiniton tagName : tagNames) { + if (setting.length() != 0) { + setting.append(";"); + } + setting.append(tagName.toSettingsFormat()); + } + ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.java deleted file mode 100755 index 91a24e7ad2..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.java +++ /dev/null @@ -1,316 +0,0 @@ -/* -* Autopsy Forensic Browser -* -* Copyright 2011-2016 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.casemodule.services; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.swing.DefaultListModel; -import javax.swing.JOptionPane; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import org.netbeans.spi.options.OptionsPanelController; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.corecomponents.OptionsPanel; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.ModuleSettings; - -/** - * A panel to allow the user to create new tag names or to delete tag names that - * user has created in the past. List of user tag names is maintained in a - * properties file, able to be used across cases. Potentially room to add other - * tag name options in the future. - */ -final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsPanel { - - private static final Logger logger = Logger.getLogger(TagNamesSettingsPanel.class.getName()); - - private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS - private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS - - private static final String DEFAULT_DESCRIPTION = ""; - private static final String DEFAULT_COLOR_STRING = "NONE"; - - private DefaultListModel tagNamesListModel; - private List tagNames; - - /** - * Creates new form TagsManagerOptionsPanel - */ - TagNamesSettingsPanel() { - initComponents(); - customizeComponents(); - } - - private void customizeComponents() { - tagNamesListModel = new DefaultListModel<>(); - tagNamesList.setModel(tagNamesListModel); - tagNames = new ArrayList<>(); - tagNamesList.addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - enableButtons(); - } - }); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jPanel1 = new javax.swing.JPanel(); - panelDescriptionLabel = new javax.swing.JLabel(); - jSplitPane1 = new javax.swing.JSplitPane(); - modifyTagNameListPanel = new javax.swing.JPanel(); - tagNamesListLabel = new javax.swing.JLabel(); - jScrollPane1 = new javax.swing.JScrollPane(); - tagNamesList = new javax.swing.JList<>(); - newTagNameButton = new javax.swing.JButton(); - deleteTagNameButton = new javax.swing.JButton(); - tagNameAdditionalPanel = new javax.swing.JPanel(); - - jPanel1.setPreferredSize(new java.awt.Dimension(750, 500)); - - org.openide.awt.Mnemonics.setLocalizedText(panelDescriptionLabel, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.panelDescriptionLabel.text")); // NOI18N - - jSplitPane1.setDividerLocation(400); - jSplitPane1.setDividerSize(1); - - org.openide.awt.Mnemonics.setLocalizedText(tagNamesListLabel, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.tagNamesListLabel.text")); // NOI18N - - jScrollPane1.setViewportView(tagNamesList); - - newTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add-tag.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(newTagNameButton, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.newTagNameButton.text")); // NOI18N - newTagNameButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - newTagNameButtonActionPerformed(evt); - } - }); - - deleteTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete-tag.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(deleteTagNameButton, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.deleteTagNameButton.text")); // NOI18N - deleteTagNameButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - deleteTagNameButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout modifyTagNameListPanelLayout = new javax.swing.GroupLayout(modifyTagNameListPanel); - modifyTagNameListPanel.setLayout(modifyTagNameListPanelLayout); - modifyTagNameListPanelLayout.setHorizontalGroup( - modifyTagNameListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(modifyTagNameListPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(modifyTagNameListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tagNamesListLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(modifyTagNameListPanelLayout.createSequentialGroup() - .addComponent(newTagNameButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteTagNameButton) - .addGap(0, 113, Short.MAX_VALUE)) - .addComponent(jScrollPane1)) - .addContainerGap()) - ); - modifyTagNameListPanelLayout.setVerticalGroup( - modifyTagNameListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(modifyTagNameListPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(tagNamesListLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(modifyTagNameListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newTagNameButton) - .addComponent(deleteTagNameButton)) - .addContainerGap()) - ); - - jSplitPane1.setLeftComponent(modifyTagNameListPanel); - - javax.swing.GroupLayout tagNameAdditionalPanelLayout = new javax.swing.GroupLayout(tagNameAdditionalPanel); - tagNameAdditionalPanel.setLayout(tagNameAdditionalPanelLayout); - tagNameAdditionalPanelLayout.setHorizontalGroup( - tagNameAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 356, Short.MAX_VALUE) - ); - tagNameAdditionalPanelLayout.setVerticalGroup( - tagNameAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 456, Short.MAX_VALUE) - ); - - jSplitPane1.setRightComponent(tagNameAdditionalPanel); - - javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); - jPanel1.setLayout(jPanel1Layout); - jPanel1Layout.setHorizontalGroup( - jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jSplitPane1) - .addComponent(panelDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) - ); - jPanel1Layout.setVerticalGroup( - jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(panelDescriptionLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSplitPane1) - .addContainerGap()) - ); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 778, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - - private void newTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagNameButtonActionPerformed - NewUserTagNameDialog dialog = new NewUserTagNameDialog(); - NewUserTagNameDialog.BUTTON_PRESSED result = dialog.getResult(); - if (result == NewUserTagNameDialog.BUTTON_PRESSED.OK) { - String newTagDisplayName = dialog.getTagName(); - UserTagName newTagName = new UserTagName(newTagDisplayName, DEFAULT_DESCRIPTION, DEFAULT_COLOR_STRING); - /* - * If tag name already exists, don't add the tag name. - */ - if (tagNames.contains(newTagName)) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message"), - NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title"), - JOptionPane.INFORMATION_MESSAGE); - } else { - tagNames.add(newTagName); - updateTagNamesListModel(); - /* - * Set the selection to the tag name that was just added. - */ - int index = tagNames.indexOf(newTagName); - tagNamesList.setSelectedIndex(index); - enableButtons(); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - } - } - }//GEN-LAST:event_newTagNameButtonActionPerformed - - private void deleteTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagNameButtonActionPerformed - UserTagName tagName = tagNamesList.getSelectedValue(); - tagNames.remove(tagName); - updateTagNamesListModel(); - enableButtons(); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_deleteTagNameButtonActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton deleteTagNameButton; - private javax.swing.JPanel jPanel1; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JSplitPane jSplitPane1; - private javax.swing.JPanel modifyTagNameListPanel; - private javax.swing.JButton newTagNameButton; - private javax.swing.JLabel panelDescriptionLabel; - private javax.swing.JPanel tagNameAdditionalPanel; - private javax.swing.JList tagNamesList; - private javax.swing.JLabel tagNamesListLabel; - // End of variables declaration//GEN-END:variables - - /** - * Updates the tag names model for the tag names list component. - */ - private void updateTagNamesListModel() { - tagNamesListModel.clear(); - Set tagNameSet = new HashSet<>(); - tagNameSet.addAll(tagNames); - tagNames.clear(); - tagNames.addAll(tagNameSet); - Collections.sort(tagNames); - for (UserTagName tagName : tagNames) { - tagNamesListModel.addElement(tagName); - } - } - - /** - * Stores tag name changes in the properties file, called when OK or Apply - * is selected in the options panel. - * - * Adds all new tag names to the case database for displaying usable tag - * names when tagging. - */ - @Override - public void store() { - StringBuilder setting = new StringBuilder(); - for (UserTagName tagName : tagNames) { - if (setting.length() != 0) { - setting.append(";"); - } - setting.append(tagName.toSettingsFormat()); - } - ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); - if (Case.isCaseOpen()) { - Case.getCurrentCase().getServices().getTagsManager().storeNewUserTagNames(tagNames); - } - } - - /** - * Updates the tag names list component with tag names from the properties - * file. - */ - @Override - public void load() { - String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - tagNames.clear(); - if (null != setting && !setting.isEmpty()) { - List tagNameTuples = Arrays.asList(setting.split(";")); - for (String tagNameTuple : tagNameTuples) { - String[] tagNameAttributes = tagNameTuple.split(","); - tagNames.add(new UserTagName(tagNameAttributes[0], tagNameAttributes[1], tagNameAttributes[2])); - } - } - updateTagNamesListModel(); - enableButtons(); - } - - /** - * Only enable delete button when there is a tag name selected in the list. - */ - private void enableButtons() { - boolean ruleIsSelected = tagNamesList.getSelectedIndex() != -1; - deleteTagNameButton.setEnabled(ruleIsSelected); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form similarity index 90% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.form rename to Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form index 4412f20274..1f81b4d3aa 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form @@ -65,7 +65,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -90,7 +90,7 @@ - + @@ -107,7 +107,7 @@ - + @@ -121,10 +121,10 @@ - + - + @@ -140,9 +140,10 @@ + - + @@ -153,7 +154,7 @@ - + @@ -166,7 +167,7 @@ - + @@ -175,7 +176,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java new file mode 100755 index 0000000000..4f761062e8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java @@ -0,0 +1,272 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.casemodule.services; + +import java.util.Set; +import java.util.TreeSet; +import javax.swing.DefaultListModel; +import javax.swing.JOptionPane; +import javax.swing.event.ListSelectionEvent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.corecomponents.OptionsPanel; +import org.sleuthkit.datamodel.TagName; + +/** + * A panel to allow the user to create and delete custom tag types. + */ +final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { + + private static final long serialVersionUID = 1L; + private static final String DEFAULT_DESCRIPTION = ""; + private static final TagName.HTML_COLOR DEFAULT_COLOR = TagName.HTML_COLOR.NONE; + private final DefaultListModel tagTypesListModel; + private Set tagTypes; + + /** + * Creates new form TagsManagerOptionsPanel + */ + TagOptionsPanel() { + tagTypesListModel = new DefaultListModel<>(); + tagTypes = new TreeSet<>(TagNameDefiniton.getTagNameDefinitions()); + initComponents(); + customizeComponents(); + } + + private void customizeComponents() { + tagNamesList.setModel(tagTypesListModel); + tagNamesList.addListSelectionListener((ListSelectionEvent event) -> { + enableButtons(); + }); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + panelDescriptionLabel = new javax.swing.JLabel(); + jSplitPane1 = new javax.swing.JSplitPane(); + modifyTagTypesListPanel = new javax.swing.JPanel(); + tagTypesListLabel = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); + tagNamesList = new javax.swing.JList<>(); + newTagNameButton = new javax.swing.JButton(); + deleteTagNameButton = new javax.swing.JButton(); + tagTypesAdditionalPanel = new javax.swing.JPanel(); + + jPanel1.setPreferredSize(new java.awt.Dimension(750, 500)); + + org.openide.awt.Mnemonics.setLocalizedText(panelDescriptionLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.panelDescriptionLabel.text")); // NOI18N + + jSplitPane1.setDividerLocation(400); + jSplitPane1.setDividerSize(1); + + org.openide.awt.Mnemonics.setLocalizedText(tagTypesListLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.tagTypesListLabel.text")); // NOI18N + + tagNamesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + jScrollPane1.setViewportView(tagNamesList); + + newTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add-tag.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(newTagNameButton, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.newTagNameButton.text")); // NOI18N + newTagNameButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newTagNameButtonActionPerformed(evt); + } + }); + + deleteTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete-tag.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(deleteTagNameButton, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.deleteTagNameButton.text")); // NOI18N + deleteTagNameButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteTagNameButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout modifyTagTypesListPanelLayout = new javax.swing.GroupLayout(modifyTagTypesListPanel); + modifyTagTypesListPanel.setLayout(modifyTagTypesListPanelLayout); + modifyTagTypesListPanelLayout.setHorizontalGroup( + modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tagTypesListLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() + .addComponent(newTagNameButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteTagNameButton) + .addGap(0, 113, Short.MAX_VALUE)) + .addComponent(jScrollPane1)) + .addContainerGap()) + ); + modifyTagTypesListPanelLayout.setVerticalGroup( + modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(tagTypesListLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(newTagNameButton) + .addComponent(deleteTagNameButton)) + .addContainerGap()) + ); + + jSplitPane1.setLeftComponent(modifyTagTypesListPanel); + + javax.swing.GroupLayout tagTypesAdditionalPanelLayout = new javax.swing.GroupLayout(tagTypesAdditionalPanel); + tagTypesAdditionalPanel.setLayout(tagTypesAdditionalPanelLayout); + tagTypesAdditionalPanelLayout.setHorizontalGroup( + tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 356, Short.MAX_VALUE) + ); + tagTypesAdditionalPanelLayout.setVerticalGroup( + tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 456, Short.MAX_VALUE) + ); + + jSplitPane1.setRightComponent(tagTypesAdditionalPanel); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jSplitPane1) + .addComponent(panelDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(panelDescriptionLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSplitPane1) + .addContainerGap()) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 778, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void newTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagNameButtonActionPerformed + NewTagNameDialog dialog = new NewTagNameDialog(); + NewTagNameDialog.BUTTON_PRESSED result = dialog.getResult(); + if (result == NewTagNameDialog.BUTTON_PRESSED.OK) { + String newTagDisplayName = dialog.getTagName(); + TagNameDefiniton newTagType = new TagNameDefiniton(newTagDisplayName, DEFAULT_DESCRIPTION, DEFAULT_COLOR); + /* + * If tag name already exists, don't add the tag name. + */ + if (!tagTypes.contains(newTagType)) { + tagTypes.add(newTagType); + updateTagNamesListModel(); + tagNamesList.setSelectedValue(newTagType, true); + enableButtons(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } else { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(TagOptionsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message"), + NbBundle.getMessage(TagOptionsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title"), + JOptionPane.INFORMATION_MESSAGE); + } + } + }//GEN-LAST:event_newTagNameButtonActionPerformed + + private void deleteTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagNameButtonActionPerformed + TagNameDefiniton tagName = tagNamesList.getSelectedValue(); + tagTypes.remove(tagName); + updateTagNamesListModel(); + enableButtons(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_deleteTagNameButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton deleteTagNameButton; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JSplitPane jSplitPane1; + private javax.swing.JPanel modifyTagTypesListPanel; + private javax.swing.JButton newTagNameButton; + private javax.swing.JLabel panelDescriptionLabel; + private javax.swing.JList tagNamesList; + private javax.swing.JPanel tagTypesAdditionalPanel; + private javax.swing.JLabel tagTypesListLabel; + // End of variables declaration//GEN-END:variables + + /** + * Updates the tag names model for the tag names list component. + */ + private void updateTagNamesListModel() { + tagTypesListModel.clear(); + for (TagNameDefiniton tagName : tagTypes) { + tagTypesListModel.addElement(tagName); + } + } + + /** + * Loads the stored custom tag types. + */ + @Override + public void load() { + tagTypes = new TreeSet<>(TagNameDefiniton.getTagNameDefinitions()); + updateTagNamesListModel(); + enableButtons(); + } + + /** + * Stores the custom tag types. + */ + @Override + public void store() { + TagNameDefiniton.setTagNameDefinitions(tagTypes); + } + + /** + * Enables the button components based on the state of the tag types list + * component. + */ + private void enableButtons() { + /* + * Only enable the delete button when there is a tag type selected in + * the tag types JList. + */ + deleteTagNameButton.setEnabled(tagNamesList.getSelectedIndex() != -1); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index 7798d3cf3a..46982397f8 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -1,26 +1,25 @@ /* -* Autopsy Forensic Browser -* -* Copyright 2011-2016 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. + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.casemodule.services; import java.io.Closeable; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -31,7 +30,6 @@ import java.util.logging.Level; 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; import org.sleuthkit.datamodel.Content; @@ -41,22 +39,19 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** - * A per case Autopsy service that manages the creation, updating, and deletion - * of tags applied to content and blackboard artifacts by users. + * A per case Autopsy service that manages the addition of content and artifact + * tags to the case database. */ public class TagsManager implements Closeable { - private static final Logger logger = Logger.getLogger(TagsManager.class.getName()); - private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS - private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS - private SleuthkitCase caseDb; - private final HashMap uniqueTagNames = new HashMap<>(); - private boolean tagNamesLoaded = false; + private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); + @NbBundle.Messages("TagsManager.predefTagNames.bookmark.text=Bookmark") + private static final Set STANDARD_TAG_DISPLAY_NAMES = new HashSet<>(Arrays.asList(Bundle.TagsManager_predefTagNames_bookmark_text())); + private final SleuthkitCase caseDb; /** - * Constructs a per case Autopsy service that manages the creation, - * updating, and deletion of tags applied to content and blackboard - * artifacts by users. + * Constructs a per case Autopsy service that manages the addition of + * content and artifact tags to the case database. * * @param caseDb The case database. */ @@ -67,205 +62,150 @@ public class TagsManager implements Closeable { /** * Gets a list of all tag names currently in the case database. * - * @return A list, possibly empty, of TagName data transfer objects (DTOs). + * @return A list, possibly empty, of TagName objects. * - * @throws TskCoreException If there is an error reading from the case - * database. + * @throws TskCoreException If there is an error querying the case database. */ - public synchronized List getAllTagNames() throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); + public List getAllTagNames() throws TskCoreException { return caseDb.getAllTagNames(); } /** - * Gets a mapping of user tag name display names to TagName DTOs if they - * have been added to the database. Otherwise, the display name maps to - * null. + * Gets a list of all tag names currently in use in the case database for + * tagging content or artifacts. * - * @return A map of String display name to TagName DTO, TagName may be null - */ - public synchronized Map getUserTagNamesMap() { - lazyLoadExistingTagNames(); - Map tagNamesMap = new HashMap<>(); - String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - if (null != setting && !setting.isEmpty()) { - List tagNameTuples = Arrays.asList(setting.split(";")); - for (String tagNameTuple : tagNameTuples) { - String[] tagNameAttributes = tagNameTuple.split(","); - tagNamesMap.put(tagNameAttributes[0], uniqueTagNames.get(tagNameAttributes[0])); - } - } - return tagNamesMap; - } - - /** - * Gets a mapping of predefined tag names to their TagName DTOs. Currently - * only for the bookmark tag. - * - * @return A map of String display name to TagName DTO - */ - public synchronized Map getPredefinedTagNamesMap() { - Map tagNamesMap = new HashMap<>(); - TagName bookmarkTagName = uniqueTagNames.get(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text")); - tagNamesMap.put(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), bookmarkTagName); - return tagNamesMap; - } - - /** - * Gets a mapping of the display names of predefined tag names and tag names - * that are in use to their TagName DTOs. - * - * @return A map of String display name to TagName DTO - * - * @throws TskCoreException If there is an error reading from the case - * database. - */ - public synchronized Map getTagNamesInUseMap() throws TskCoreException { - List tagNames = getTagNamesInUse(); - Map tagNamesMap = new HashMap<>(); - for (TagName tagName : tagNames) { - tagNamesMap.put(tagName.getDisplayName(), tagName); - } - return tagNamesMap; - } - - /** - * Gets a list of all tag names currently in use for tagging content or - * artifacts. + * @return A list, possibly empty, of TagName objects. * - * @return A list, possibly empty, of TagName data transfer objects (DTOs). - * - * @throws TskCoreException If there is an error reading from the case - * database. + * @throws TskCoreException If there is an error querying the case database. */ - public synchronized List getTagNamesInUse() throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); + public List getTagNamesInUse() throws TskCoreException { return caseDb.getTagNamesInUse(); } /** - * Checks whether a tag name with a given display name exists. + * Gets a map of tag display names to tag name entries in the case database. + * It has keys for the display names of the standard tag types, the current + * user's custom tag types, and the tags in the case database. The value for + * a given key will be null if the corresponding tag type is defined, but a + * tag name entry has not yet added to the case database. In that case, + * addTagName may be called to add the tag name entry. * - * @param tagDisplayName The display name to check. + * @return A map of tag display names to possibly null TagName object + * references. * - * @return True or false. + * @throws TskCoreException if there is an error querying the case database. */ - public synchronized boolean tagNameExists(String tagDisplayName) { - lazyLoadExistingTagNames(); - return uniqueTagNames.containsKey(tagDisplayName) && - (uniqueTagNames.get(tagDisplayName) != null); + public synchronized Map getDisplayNamesToTagNamesMap() throws TskCoreException { + /** + * Order is important here. The keys (display names) for the standard + * tag types and current user's custom tag types are added to the map + * first, with null TagName values. If tag name entries exist for those + * keys, loading of the tag names from the database supplies the missing + * values. + * + * Note that creating the map on demand increases the probability that + * the display names of newly added custom tag types and the display + * names of tags added to a multi-user case by other users appear in the + * map. + */ + Map tagNames = new HashMap<>(); + tagNames.put(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), null); + Set customTypes = TagNameDefiniton.getTagNameDefinitions(); + for (TagNameDefiniton tagType : customTypes) { + tagNames.put(tagType.getDisplayName(), null); + } + for (TagName tagName : caseDb.getAllTagNames()) { + tagNames.put(tagName.getDisplayName(), tagName); + } + return new HashMap<>(tagNames); } /** - * Adds a new tag name to the current case and to the tags settings. + * Adds a tag name entry to the case database and adds a corresponding tag + * type to the current user's custom tag types. * - * @param displayName The display name for the new tag name. + * @param displayName The display name for the new tag type. * - * @return A TagName data transfer object (DTO) representing the new tag - * name. + * @return A TagName representing the tag name database entry that can be + * used to add instances of the tag type to the case database. * - * @throws TagNameAlreadyExistsException If the tag name would be a - * duplicate. + * @throws TagNameAlreadyExistsException If the tag name already exists in + * the case database. * @throws TskCoreException If there is an error adding the tag - * to the case database. + * name to the case database. */ - public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } + public synchronized TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException { return addTagName(displayName, "", TagName.HTML_COLOR.NONE); } /** - * Adds a new tag name to the current case and to the tags settings. + * Adds a tag name entry to the case database and adds a corresponding tag + * type to the current user's custom tag types. * - * @param displayName The display name for the new tag name. - * @param description The description for the new tag name. + * @param displayName The display name for the new tag type. + * @param description The description for the new tag type. * - * @return A TagName data transfer object (DTO) representing the new tag - * name. + * @return A TagName object that can be used to add instances of the tag + * type to the case database. * - * @throws TagNameAlreadyExistsException If the tag name would be a - * duplicate. + * @throws TagNameAlreadyExistsException If the tag name already exists in + * the case database. * @throws TskCoreException If there is an error adding the tag - * to the case database. + * name to the case database. */ - public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } + public synchronized TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException { return addTagName(displayName, description, TagName.HTML_COLOR.NONE); } /** - * Adds a new tag name to the current case and to the tags settings. + * Adds a tag name entry to the case database and adds a corresponding tag + * type to the current user's custom tag types. * - * @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. + * @param displayName The display name for the new tag type. + * @param description The description for the new tag type. + * @param color The color to associate with the new tag type. * - * @return A TagName data transfer object (DTO) representing the new tag - * name. + * @return A TagName object that can be used to add instances of the tag + * type to the case database. * - * @throws TagNameAlreadyExistsException If the tag name would be a - * duplicate. + * @throws TagNameAlreadyExistsException If the tag name already exists. * @throws TskCoreException If there is an error adding the tag - * to the case database. + * name to the case database. */ public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - - lazyLoadExistingTagNames(); - - /* - * It is possible user is trying to add back a tag after having deleted - * it. It is also possible a user tag name was never added to the - * database. - */ - if (uniqueTagNames.containsKey(displayName)) { - if (uniqueTagNames.get(displayName) != null) { - TagName existingTagName = uniqueTagNames.get(displayName); - long count = getContentTagsCountByTagName(existingTagName) + getBlackboardArtifactTagsCountByTagName(existingTagName); - if (count == 0) { - addNewTagNameToTagsSettings(existingTagName); - return existingTagName; - } else { + try { + TagName tagName = caseDb.addTagName(displayName, description, color); + if (!STANDARD_TAG_DISPLAY_NAMES.contains(displayName)) { + Set customTypes = TagNameDefiniton.getTagNameDefinitions(); + customTypes.add(new TagNameDefiniton(displayName, description, color)); + TagNameDefiniton.setTagNameDefinitions(customTypes); + } + return tagName; + } catch (TskCoreException ex) { + List existingTagNames = caseDb.getAllTagNames(); + for (TagName tagName : existingTagNames) { + if (tagName.getDisplayName().equals(displayName)) { throw new TagNameAlreadyExistsException(); } } + throw ex; } - - TagName newTagName = caseDb.addTagName(displayName, description, color); - uniqueTagNames.put(newTagName.getDisplayName(), newTagName); - - addNewTagNameToTagsSettings(newTagName); - - return newTagName; } /** * Tags a content object. * * @param content The content to tag. - * @param tagName The name to use for the tag. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * - * @return A ContentTag data transfer object (DTO) representing the new tag. + * @return A ContentTag object representing the new tag. * * @throws TskCoreException If there is an error adding the tag to the case * database. */ public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } return addContentTag(content, tagName, "", -1, -1); } @@ -273,18 +213,17 @@ public class TagsManager implements Closeable { * Tags a content object. * * @param content The content to tag. - * @param tagName The name to use for the tag. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * @param comment A comment to store with the tag. * - * @return A ContentTag data transfer object (DTO) representing the new tag. + * @return A ContentTag object representing the new tag. * * @throws TskCoreException If there is an error adding the tag to the case * database. */ public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } return addContentTag(content, tagName, comment, -1, -1); } @@ -292,56 +231,25 @@ public class TagsManager implements Closeable { * Tags a content object or a section of a content object. * * @param content The content to tag. - * @param tagName The name to use for the tag. + * @param tagName The representation of the desired tag type in the + * case database, which can be obtained by calling + * getTagNames and/or addTagName. * @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. + * @return A ContentTag object representing the new tag. * - * @throws IllegalArgumentException If a requested byte offset is out of - * range. - * @throws TskCoreException If there is an error adding the tag to - * the case database. + * @throws TskCoreException If there is an error adding the tag to the case + * database. */ - public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } + public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException { ContentTag tag; - synchronized (this) { - lazyLoadExistingTagNames(); - - if (null == comment) { - throw new IllegalArgumentException("Passed null comment argument"); - } - - 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)); - } - - if (endByteOffset > content.getSize() - 1) { - throw new IllegalArgumentException( - NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endByteOffsetOOR.msg", - endByteOffset, content.getSize() - 1)); - } - - if (endByteOffset < beginByteOffset) { - throw new IllegalArgumentException( - NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endLTbegin.msg")); - } - } - - tag = caseDb.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset); - } - + tag = caseDb.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset); try { Case.getCurrentCase().notifyContentTagAdded(tag); } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.addContentTag.noCaseWarning"), ex); + LOGGER.log(Level.SEVERE, "Added a tag to a closed case", ex); } return tag; } @@ -355,18 +263,11 @@ public class TagsManager implements Closeable { * case database. */ public void deleteContentTag(ContentTag tag) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - synchronized (this) { - lazyLoadExistingTagNames(); - caseDb.deleteContentTag(tag); - } - + caseDb.deleteContentTag(tag); try { Case.getCurrentCase().notifyContentTagDeleted(tag); } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteContentTag.noCaseWarning"), ex); + LOGGER.log(Level.SEVERE, "Deleted a tag from a closed case", ex); } } @@ -379,17 +280,15 @@ public class TagsManager implements Closeable { * case database. */ public synchronized List getAllContentTags() throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getAllContentTags(); } /** * Gets content tags count by tag name. * - * @param tagName The tag name of interest. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * * @return A count of the content tags with the specified tag name. * @@ -397,29 +296,21 @@ public class TagsManager implements Closeable { * the case database. */ public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getContentTagsCountByTagName(tagName); } /** * Gets a content tag by tag id. * - * @param tagID The tag id of interest. + * @param tagId The tag id of interest. * * @return The content tag with the specified tag id. * * @throws TskCoreException If there is an error getting the tag from the * case database. */ - public synchronized ContentTag getContentTagByTagID(long tagID) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); - return caseDb.getContentTagByID(tagID); + public synchronized ContentTag getContentTagByTagID(long tagId) throws TskCoreException { + return caseDb.getContentTagByID(tagId); } /** @@ -434,10 +325,6 @@ public class TagsManager implements Closeable { * case database. */ public synchronized List getContentTagsByTagName(TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getContentTagsByTagName(tagName); } @@ -447,172 +334,136 @@ public class TagsManager implements Closeable { * @param content The content of interest. * * @return A list, possibly empty, of the tags that have been applied to the - * artifact. + * content. * * @throws TskCoreException If there is an error getting the tags from the * case database. */ public synchronized List getContentTagsByContent(Content content) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getContentTagsByContent(content); } /** - * Tags a blackboard artifact object. + * Tags an artifact. * - * @param artifact The blackboard artifact to tag. - * @param tagName The name to use for the tag. + * @param artifact The artifact to tag. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * - * @return A BlackboardArtifactTag data transfer object (DTO) representing - * the new tag. + * @return A BlackboardArtifactTag object representing the new tag. * * @throws TskCoreException If there is an error adding the tag to the case * database. */ - public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } + public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException { return addBlackboardArtifactTag(artifact, tagName, ""); } /** - * Tags a blackboard artifact object. + * Tags an artifact. * - * @param artifact The blackboard artifact to tag. - * @param tagName The name to use for the tag. + * @param artifact The artifact to tag. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * @param comment A comment to store with the tag. * - * @return A BlackboardArtifactTag data transfer object (DTO) representing - * the new tag. + * @return A BlackboardArtifactTag object representing the new tag. * * @throws TskCoreException If there is an error adding the tag to the case * database. */ - public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - BlackboardArtifactTag tag; - synchronized (this) { - lazyLoadExistingTagNames(); - if (null == comment) { - throw new IllegalArgumentException("Passed null comment argument"); - } - tag = caseDb.addBlackboardArtifactTag(artifact, tagName, comment); - } - + public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { + BlackboardArtifactTag tag = caseDb.addBlackboardArtifactTag(artifact, tagName, comment); try { Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(tag); } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.addBlackboardArtifactTag.noCaseWarning"), ex); + LOGGER.log(Level.SEVERE, "Added a tag to a closed case", ex); } return tag; } /** - * Deletes a blackboard artifact tag. + * Deletes an artifact tag. * * @param tag The tag to delete. * * @throws TskCoreException If there is an error deleting the tag from the * case database. */ - public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - synchronized (this) { - lazyLoadExistingTagNames(); - caseDb.deleteBlackboardArtifactTag(tag); - } - + public synchronized void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { + caseDb.deleteBlackboardArtifactTag(tag); try { Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag); } catch (IllegalStateException ex) { - logger.log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteBlackboardArtifactTag.noCaseWarning"), ex); + LOGGER.log(Level.SEVERE, "Deleted a tag from a closed case", ex); } } /** - * Gets all blackboard artifact tags for the current case. + * Gets all artifact tags for the current case. * - * @return A list, possibly empty, of blackboard artifact tags. + * @return A list, possibly empty, of artifact tags. * * @throws TskCoreException If there is an error getting the tags from the * case database. */ public synchronized List getAllBlackboardArtifactTags() throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getAllBlackboardArtifactTags(); } /** - * Gets blackboard artifact tags count by tag name. + * Gets an artifact tags count by tag name. * - * @param tagName The tag name of interest. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * - * @return A count of the blackboard artifact tags with the specified tag - * name. + * @return A count of the artifact tags with the specified tag name. * * @throws TskCoreException If there is an error getting the tags count from * the case database. */ public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getBlackboardArtifactTagsCountByTagName(tagName); } /** - * Gets a blackboard artifact tag by tag id. + * Gets an artifact tag by tag id. * - * @param tagID The tag id of interest. + * @param tagId The tag id of interest. * - * @return the blackboard artifact tag with the specified tag id. + * @return The artifact tag with the specified tag id. * * @throws TskCoreException If there is an error getting the tag from the * case database. */ - public synchronized BlackboardArtifactTag getBlackboardArtifactTagByTagID(long tagID) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); - return caseDb.getBlackboardArtifactTagByID(tagID); + public synchronized BlackboardArtifactTag getBlackboardArtifactTagByTagID(long tagId) throws TskCoreException { + return caseDb.getBlackboardArtifactTagByID(tagId); } /** - * Gets blackboard artifact tags by tag name. + * Gets artifact tags by tag name. * - * @param tagName The tag name of interest. + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. * - * @return A list, possibly empty, of the blackboard artifact tags with the - * specified tag name. + * @return A list, possibly empty, of the artifact tags with the specified + * tag name. * * @throws TskCoreException If there is an error getting the tags from the * case database. */ public synchronized List getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getBlackboardArtifactTagsByTagName(tagName); } /** - * Gets blackboard artifact tags for a particular blackboard artifact. + * Gets artifact tags for a particular artifact. * - * @param artifact The blackboard artifact of interest. + * @param artifact The artifact of interest. * * @return A list, possibly empty, of the tags that have been applied to the * artifact. @@ -621,129 +472,15 @@ public class TagsManager implements Closeable { * case database. */ public synchronized List getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException { - if (null == caseDb) { - throw new TskCoreException("Tags manager has been closed"); - } - lazyLoadExistingTagNames(); return caseDb.getBlackboardArtifactTagsByArtifact(artifact); } - /** - * Closes the tags manager, saving the available tag names to secondary - * storage. - * - * @throws IOException If there is a problem closing the tags manager. - * @deprecated Tags manager clients should not close the tags manager. - */ - @Override - @Deprecated - public synchronized void close() throws IOException { - caseDb = null; - } - - /** - * Populates the tag names collection and the tag names table in the case - * database with the existing tag names from all sources. - */ - private void lazyLoadExistingTagNames() { - if (!tagNamesLoaded) { - addTagNamesFromCurrentCase(); - addPredefinedTagNames(); - addTagNamesFromTagsSettings(); - tagNamesLoaded = true; - } else { - // Reload case db tag names in case another user has added some. - addTagNamesFromCurrentCase(); - } - } - - /** - * Adds any tag names that are in the case database to the tag names - * collection. - */ - private void addTagNamesFromCurrentCase() { - try { - List currentTagNames = caseDb.getAllTagNames(); - for (TagName tagName : currentTagNames) { - uniqueTagNames.put(tagName.getDisplayName(), tagName); - } - } catch (TskCoreException ex) { - Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex); //NON-NLS - } - } - - /** - * Adds any tag names that are in the properties file to the tag names - * collection and to the case database. The properties file is used to make - * it possible to use tag names across cases. - */ - private void addTagNamesFromTagsSettings() { - String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - if (null != setting && !setting.isEmpty()) { - // Read the tag name setting and break it into tag name tuples. - 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) { - String[] tagNameAttributes = tagNameTuple.split(","); - if (!uniqueTagNames.containsKey(tagNameAttributes[0])) { - uniqueTagNames.put(tagNameAttributes[0], null); - } - } - } - } - - /** - * Adds the standard tag names to the tag names collection. - */ - private void addPredefinedTagNames() { - if (!uniqueTagNames.containsKey(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"))) { - try { - TagName tagName = caseDb.addTagName( - NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), "", TagName.HTML_COLOR.NONE); - uniqueTagNames.put(tagName.getDisplayName(), tagName); - } catch (TskCoreException ex) { - Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add standard 'Bookmark' tag name to case database", ex); //NON-NLS - } - } - } - - /** - * Adds any user defined tag name to the case db and also to uniqueTagNames, - * to allow user tag names to be displayed while tagging. - * - * @param userTagNames a List of UserTagName objects to be potentially added - */ - void storeNewUserTagNames(List userTagNames) { - lazyLoadExistingTagNames(); - for (UserTagName utn : userTagNames) { - if (!uniqueTagNames.containsKey(utn.getDisplayName())) { - uniqueTagNames.put(utn.getDisplayName(), null); - } - } - } - - /** - * Adds a new tag name to the settings file, used when user creates a new - * tag name. - */ - private void addNewTagNameToTagsSettings(TagName tagName) { - String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - if (setting == null || setting.isEmpty()) { - setting = ""; - } else { - setting += ";"; - } - setting += tagName.getDisplayName() + "," + tagName.getDescription() + "," + tagName.getColor().toString(); - ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting); - } - /** * Returns true if the tag display name contains an illegal character. Used * after a tag display name is retrieved from user input. * * @param content Display name of the tag being added. + * * @return boolean indicating whether the name has an invalid character. */ public static boolean containsIllegalCharacters(String content) { @@ -757,6 +494,7 @@ public class TagsManager implements Closeable { || content.contains("|") || content.contains(",") || content.contains(";")); + } /** @@ -767,4 +505,36 @@ public class TagsManager implements Closeable { private static final long serialVersionUID = 1L; } + /** + * Checks whether a tag name with a given display name exists in the case + * database. + * + * @param tagDisplayName The display name. + * + * @return True or false. + * + * @deprecated Not reliable for multi-user cases. + */ + @Deprecated + public synchronized boolean tagNameExists(String tagDisplayName) { + try { + Map tagNames = getDisplayNamesToTagNamesMap(); + return tagNames.containsKey(tagDisplayName) && (tagNames.get(tagDisplayName) != null); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error querying case database for tag names", ex); + return false; + } + } + + /** + * Closes the tags manager. + * + * @throws IOException If there is a problem closing the tags manager. + * @deprecated Tags manager clients should not close the tags manager. + */ + @Override + @Deprecated + public synchronized void close() throws IOException { + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesOptionsPanelController.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java similarity index 85% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesOptionsPanelController.java rename to Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java index 20c478d966..3d36e8ae00 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNamesOptionsPanelController.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java @@ -28,14 +28,14 @@ import org.openide.util.Lookup; @OptionsPanelController.TopLevelRegistration( categoryName = "#OptionsCategory_Name_TagNamesOptions", - iconBase = "org/sleuthkit/autopsy/casemodule/services/tags-manager.png", + iconBase = "org/sleuthkit/autopsy/casemodule/services/tag-options-panel-icon.png", keywords = "#OptionsCategory_TagNames", keywordsCategory = "CustomTagNames", position = 8 ) -public final class TagNamesOptionsPanelController extends OptionsPanelController { +public final class TagsOptionsPanelController extends OptionsPanelController { - private TagNamesSettingsPanel panel; + private TagOptionsPanel panel; private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private boolean changed; @@ -106,15 +106,12 @@ public final class TagNamesOptionsPanelController extends OptionsPanelController pcs.removePropertyChangeListener(l); } - private TagNamesSettingsPanel getPanel() { + private TagOptionsPanel getPanel() { if (panel == null) { - panel = new TagNamesSettingsPanel(); - panel.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { - changed(); - } + panel = new TagOptionsPanel(); + panel.addPropertyChangeListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { + changed(); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/UserTagName.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/UserTagName.java deleted file mode 100755 index c761665c08..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/UserTagName.java +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Autopsy Forensic Browser -* -* Copyright 2011-2016 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.casemodule.services; - -import java.util.Objects; - -/** - * Because the DTO TagName constructor can not be called outside of its class - * package, UserTagName is used to keep track of user tag names while - * preserving properties that will potentially be implemented in the future - * (tag name description and tag name color). - */ -class UserTagName implements Comparable { - - private final String displayName; - private final String description; - private final String colorName; - - UserTagName(String displayName, String description, String colorName) { - this.displayName = displayName; - this.description = description; - this.colorName = colorName; - } - - String getDisplayName() { - return displayName; - } - - String getDescription() { - return description; - } - - String getColorName() { - return colorName; - } - - @Override - public int compareTo(UserTagName other) { - return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase()); - } - - @Override - public int hashCode() { - int hash = 7; - hash = 83 * hash + Objects.hashCode(this.displayName); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof UserTagName)) { - return false; - } - UserTagName thatTagName = (UserTagName) obj; - return this.getDisplayName().equals(thatTagName.getDisplayName()); - } - - @Override - public String toString() { - return displayName; - } - - /** - * @return A string representation of the tag name in the format that is - * used by the properties file. - */ - public String toSettingsFormat() { - return displayName + "," + description + "," + colorName; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/tags-manager.png b/Core/src/org/sleuthkit/autopsy/casemodule/services/tag-options-panel-icon.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/tags-manager.png rename to Core/src/org/sleuthkit/autopsy/casemodule/services/tag-options-panel-icon.png diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index fa59de87c1..53523be839 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -27,7 +27,6 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; @@ -68,7 +67,7 @@ public class ArtifactStringContent implements StringContent { buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS - // cycle through each attribute and display in a row in the table. + // cycle through each attribute and display in a row in the table. for (BlackboardAttribute attr : artifact.getAttributes()) { // name column @@ -78,48 +77,36 @@ public class ArtifactStringContent implements StringContent { // value column buffer.append(""); //NON-NLS - if (attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_RCVD.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID() - || attr.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()) { - long epoch = attr.getValueLong(); - String time = "0000-00-00 00:00:00"; - if (epoch != 0) { - dateFormatter.setTimeZone(getTimeZone(artifact)); - time = dateFormatter.format(new java.util.Date(epoch * 1000)); - } - buffer.append(time); - } else { - switch (attr.getAttributeType().getValueType()) { - case STRING: - String str = attr.getValueString(); - str = str.replaceAll(" ", " "); //NON-NLS - str = str.replaceAll("<", "<"); //NON-NLS - str = str.replaceAll(">", ">"); //NON-NLS - str = str.replaceAll("(\r\n|\n)", "
"); //NON-NLS - buffer.append(str); - break; - case INTEGER: - buffer.append(attr.getValueInt()); - break; - case LONG: - buffer.append(attr.getValueLong()); - break; - case DOUBLE: - buffer.append(attr.getValueDouble()); - break; - case BYTE: - buffer.append(Arrays.toString(attr.getValueBytes())); - break; - case DATETIME: - buffer.append(attr.getValueLong()); - break; - - } + switch (attr.getAttributeType().getValueType()) { + case STRING: + String str = attr.getValueString(); + str = str.replaceAll(" ", " "); //NON-NLS + str = str.replaceAll("<", "<"); //NON-NLS + str = str.replaceAll(">", ">"); //NON-NLS + str = str.replaceAll("(\r\n|\n)", "
"); //NON-NLS + buffer.append(str); + break; + case INTEGER: + buffer.append(attr.getValueInt()); + break; + case LONG: + buffer.append(attr.getValueLong()); + break; + case DOUBLE: + buffer.append(attr.getValueDouble()); + break; + case BYTE: + buffer.append(Arrays.toString(attr.getValueBytes())); + break; + case DATETIME: + long epoch = attr.getValueLong(); + String time = "0000-00-00 00:00:00"; + if (epoch != 0) { + dateFormatter.setTimeZone(getTimeZone(artifact)); + time = dateFormatter.format(new java.util.Date(epoch * 1000)); + } + buffer.append(time); + break; } if (!"".equals(attr.getContext())) { buffer.append(" (");