diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index 2c6b0fce9a..14b3c6afad 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -338,6 +338,10 @@
org.sleuthkit.autopsy.modules.vmextractor
org.sleuthkit.autopsy.progress
org.sleuthkit.autopsy.report
+<<<<<<< HEAD
+=======
+ org.sleuthkit.autopsy.tabulardatareader
+>>>>>>> 4316-text-translation-service
org.sleuthkit.autopsy.texttranslation
org.sleuthkit.datamodel
diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
index cb5f797c2b..3dc230cf21 100644
--- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
+++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
@@ -72,7 +72,8 @@ public final class UserPreferences {
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences";
-
+ public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
+
// Prevent instantiation.
private UserPreferences() {
}
@@ -244,6 +245,14 @@ public final class UserPreferences {
public static void setHideCentralRepoCommentsAndOccurrences(boolean value) {
preferences.putBoolean(HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES, value);
}
+
+ public static void setDisplayTranslationFileNames(boolean value) {
+ preferences.putBoolean(DISPLAY_TRANSLATED_NAMES, value);
+ }
+
+ public static boolean displayTranslationFileNames() {
+ return preferences.getBoolean(DISPLAY_TRANSLATED_NAMES, false);
+ }
/**
* Reads persisted case database connection info.
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
index 12ba493380..3e8e75dad7 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
@@ -192,3 +192,5 @@ ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for:
ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns to reduce loading times
ViewPreferencesPanel.deletedFilesLimitCheckbox.text=Limit to 10,000
ViewPreferencesPanel.deletedFilesLimitLabel.text=Limit number of deleted files displayed:
+ViewPreferencesPanel.fileDisplayLabel.text=Translate text in the:
+ViewPreferencesPanel.translatedNamesButton.text=Table
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
index ddeb57bd74..1f76dde4dd 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
@@ -92,6 +92,8 @@
+
+
@@ -114,9 +116,11 @@
+
+
@@ -125,14 +129,12 @@
+
-
-
-
@@ -174,9 +176,15 @@
-
+
+
+
+
-
+
+
+
+
@@ -356,6 +364,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
index 51d4120449..af9a940537 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
@@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
+import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
/**
* Panel for configuring view preferences.
@@ -44,6 +45,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public ViewPreferencesPanel(boolean immediateUpdates) {
initComponents();
this.immediateUpdates = immediateUpdates;
+
+ //If there is not Text Translator implementation, then hide these buttons
+ //from the user.
+ TextTranslationService tts = TextTranslationService.getInstance();
+ if(!tts.hasProvider()) {
+ translatedNamesButton.setVisible(false);
+ fileDisplayLabel.setVisible(false);
+ }
}
@Override
@@ -67,6 +76,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences());
deletedFilesLimitCheckbox.setSelected(DeletedFilePreferences.getDefault().getShouldLimitDeletedFiles());
+ translatedNamesButton.setSelected(UserPreferences.displayTranslationFileNames());
// Current Case Settings
boolean caseIsOpen = Case.isCaseOpen();
@@ -90,6 +100,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCheckbox.isSelected());
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected());
+ UserPreferences.setDisplayTranslationFileNames(translatedNamesButton.isSelected());
storeGroupItemsInTreeByDataSource();
@@ -142,6 +153,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
centralRepoLabel = new javax.swing.JLabel();
deletedFilesLimitCheckbox = new javax.swing.JCheckBox();
deletedFilesLimitLabel = new javax.swing.JLabel();
+ translatedNamesButton = new javax.swing.JRadioButton();
+ fileDisplayLabel = new javax.swing.JLabel();
currentCaseSettingsPanel = new javax.swing.JPanel();
groupByDataSourceCheckbox = new javax.swing.JCheckBox();
currentSessionSettingsPanel = new javax.swing.JPanel();
@@ -244,6 +257,15 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitLabel.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(translatedNamesButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.translatedNamesButton.text")); // NOI18N
+ translatedNamesButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ translatedNamesButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(fileDisplayLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.fileDisplayLabel.text")); // NOI18N
+
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
globalSettingsPanel.setLayout(globalSettingsPanelLayout);
globalSettingsPanelLayout.setHorizontalGroup(
@@ -259,6 +281,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(centralRepoLabel)
+ .addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideKnownFilesLabel)
@@ -274,9 +298,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideKnownCheckbox)
- .addComponent(viewsHideKnownCheckbox)))))
+ .addComponent(viewsHideKnownCheckbox))))
+ .addComponent(hideOtherUsersTagsLabel))
.addGap(18, 18, 18)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(fileDisplayLabel)
.addComponent(displayTimeLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
@@ -284,11 +310,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addComponent(keepCurrentViewerRadioButton)
.addComponent(useBestViewerRadioButton)
.addComponent(useGMTTimeRadioButton)
- .addComponent(useLocalTimeRadioButton)))
- .addComponent(selectFileLabel)))
- .addComponent(hideOtherUsersTagsLabel)
- .addComponent(centralRepoLabel)
- .addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(useLocalTimeRadioButton)
+ .addComponent(translatedNamesButton)))
+ .addComponent(selectFileLabel))))
.addGap(0, 10, Short.MAX_VALUE)))
.addContainerGap())
);
@@ -322,9 +346,13 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(useGMTTimeRadioButton)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(hideOtherUsersTagsLabel)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(fileDisplayLabel)
+ .addComponent(hideOtherUsersTagsLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(hideOtherUsersTagsCheckbox)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(hideOtherUsersTagsCheckbox)
+ .addComponent(translatedNamesButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(centralRepoLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@@ -535,6 +563,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_deletedFilesLimitCheckboxActionPerformed
+ private void translatedNamesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_translatedNamesButtonActionPerformed
+ if (immediateUpdates) {
+ UserPreferences.setDisplayTranslationFileNames(translatedNamesButton.isSelected());
+ } else {
+ firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+ }
+ }//GEN-LAST:event_translatedNamesButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel centralRepoLabel;
@@ -546,6 +582,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JCheckBox deletedFilesLimitCheckbox;
private javax.swing.JLabel deletedFilesLimitLabel;
private javax.swing.JLabel displayTimeLabel;
+ private javax.swing.JLabel fileDisplayLabel;
private javax.swing.JPanel globalSettingsPanel;
private javax.swing.JCheckBox groupByDataSourceCheckbox;
private javax.swing.JLabel hideKnownFilesLabel;
@@ -555,6 +592,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JLabel hideSlackFilesLabel;
private javax.swing.JRadioButton keepCurrentViewerRadioButton;
private javax.swing.JLabel selectFileLabel;
+ private javax.swing.JRadioButton translatedNamesButton;
private javax.swing.JRadioButton useBestViewerRadioButton;
private javax.swing.JRadioButton useGMTTimeRadioButton;
private javax.swing.JRadioButton useLocalTimeRadioButton;
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
index b2dd93705c..c67a93c687 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
@@ -30,6 +30,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Children;
@@ -52,8 +53,13 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
+import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
+import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
+import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
+import org.sleuthkit.autopsy.texttranslation.TranslationCallback;
+import org.sleuthkit.autopsy.texttranslation.TranslationException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
@@ -71,10 +77,15 @@ public abstract class AbstractAbstractFileNode extends A
private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName());
@NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
+
+ private static final String TRANSLATION_AVAILABLE_EVENT = "TRANSLATION_AVAILABLE";
+ private static final String NO_TRANSLATION = "";
private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
+ private String translatedFileName;
+
/**
* @param abstractFile file to wrap
*/
@@ -89,6 +100,7 @@ public abstract class AbstractAbstractFileNode extends A
IngestManager.getInstance().addIngestModuleEventListener(weakPcl);
}
}
+ translatedFileName = null;
// Listen for case events so that we can detect when the case is closed
// or when tags are added.
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
@@ -165,6 +177,9 @@ public abstract class AbstractAbstractFileNode extends A
if (event.getContentID() == content.getId()) {
updateSheet();
}
+ } else if (eventType.equals(TRANSLATION_AVAILABLE_EVENT)) {
+ this.translatedFileName = (String) evt.getNewValue();
+ updateSheet();
}
};
@@ -211,6 +226,7 @@ public abstract class AbstractAbstractFileNode extends A
"AbstractAbstractFileNode.createSheet.score.name=S",
"AbstractAbstractFileNode.createSheet.comment.name=C",
"AbstractAbstractFileNode.createSheet.count.name=O",
+ "AbstractAbstractFileNode.translateFileName=Translated Name",
"AbstractAbstractFileNode.locationColLbl=Location",
"AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time",
"AbstractAbstractFileNode.changeTimeColLbl=Change Time",
@@ -454,6 +470,41 @@ public abstract class AbstractAbstractFileNode extends A
}
}
+ /**
+ * Attempts translation on the file name by kicking off a background task to do the
+ * translation. Once the background task is done, it will fire a PropertyChangeEvent,
+ * which will force this node to refresh itself, thus updating its translated name
+ * column.
+ *
+ * @return The file names translation.
+ */
+ protected String getTranslatedFileName() {
+ //If already in complete English, don't translate.
+ if (this.content.getName().matches("^\\p{ASCII}+$")) {
+ return NO_TRANSLATION;
+ }
+
+ //If we already have a translation use that one.
+ if (translatedFileName != null) {
+ return translatedFileName;
+ }
+
+ //If not, lets fire off a background translation that will update the UI
+ //when it is done.
+ TextTranslationService tts = TextTranslationService.getInstance();
+ if (tts.hasProvider()) {
+ //Seperate out the base and ext from the contents file name.
+ String base = FilenameUtils.getBaseName(this.content.getName());
+
+ //Send only the base file name to be translated. Once the translation comes
+ //back fire a PropertyChangeEvent on this nodes PropertyChangeListener.
+ tts.translateAsynchronously(base, new TranslateFileNameCallback(this.content, weakPcl));
+ }
+
+ //In the mean time, return a blank translation.
+ return NO_TRANSLATION;
+ }
+
/**
* Fill map with AbstractFile properties
*
@@ -659,4 +710,72 @@ public abstract class AbstractAbstractFileNode extends A
return "";
}
}
+
+ /**
+ * Implements the TranslationCallback interface so that the TextTranslationService
+ * can call these methods when an asynchronous translation task is complete.
+ */
+ private class TranslateFileNameCallback implements TranslationCallback {
+ private final String ext;
+ private final String originalFileName;
+
+ private final PropertyChangeListener listener;
+
+ public TranslateFileNameCallback(AbstractFile file, PropertyChangeListener listener) {
+ this.ext = FilenameUtils.getExtension(content.getName());
+ this.originalFileName = content.getName();
+ this.listener = listener;
+ }
+
+ /**
+ * Fires a PropertyChangeEvent on this nodes PropertyChangeListener
+ * when the translation is finished. Reconstruct the file name so we
+ * can properly display the translation.
+ *
+ * @param translation Result from the translation job submitted to
+ * Text translation service.
+ */
+ @Override
+ public void onTranslationResult(String translation) {
+ //If we have no extension, then we shouldn't add the .
+ String extensionDelimiter = (ext.isEmpty()) ? "" : ".";
+
+ //Talk directly to this nodes pcl, fire an update when the translation
+ //is complete.
+ if (translation.isEmpty()) {
+ listener.propertyChange(new PropertyChangeEvent(
+ AutopsyEvent.SourceType.LOCAL.toString(),
+ TRANSLATION_AVAILABLE_EVENT, null, translation));
+ } else {
+ listener.propertyChange(new PropertyChangeEvent(
+ AutopsyEvent.SourceType.LOCAL.toString(),
+ TRANSLATION_AVAILABLE_EVENT, null,
+ translation + extensionDelimiter + ext));
+ }
+ }
+
+ /**
+ * Do nothing on a translation exception except log, the column will remain empty
+ * and there is nothing we can do as a fallback.
+ *
+ * @param ex Exception caught while translating.
+ */
+ @Override
+ public void onTranslationException(TranslationException noTranslationEx) {
+ logger.log(Level.WARNING, "Could not successfully translate file name " + originalFileName, noTranslationEx);
+ }
+
+ /**
+ * Do nothing on a no service provider exception except log, in this implemention we
+ * are only calling for translation to be done if we know that a TextTranslator
+ * service provider is already defined.
+ *
+ * @param ex
+ */
+ @Override
+ public void onNoServiceProviderException(NoServiceProviderException noServiceEx) {
+ logger.log(Level.WARNING, "Translate unsuccessful because no TextTranslator "
+ + "implementation was provided.", noServiceEx);
+ }
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
index 9a322446bc..7269a09e2c 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
@@ -320,7 +320,7 @@ public class BlackboardArtifactNode extends AbstractContentNode {
}
}
- @Override
public T accept(ContentNodeVisitor visitor) {
return visitor.visit(this);
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java
index 411cd464ec..37f1d1a136 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java
@@ -38,6 +38,59 @@ public class LocalDirectoryNode extends SpecialDirectoryNode {
}
@Override
+<<<<<<< HEAD
+=======
+ @NbBundle.Messages({
+ "LocalDirectoryNode.createSheet.name.name=Name",
+ "LocalDirectoryNode.createSheet.name.displayName=Name",
+ "LocalDirectoryNode.createSheet.name.desc=no description",
+ "LocalDirectoryNode.createSheet.noDesc=no description"})
+ protected Sheet createSheet() {
+ Sheet sheet = super.createSheet();
+ Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
+ if (sheetSet == null) {
+ sheetSet = Sheet.createPropertiesSet();
+ sheet.put(sheetSet);
+ }
+
+ List tags = getContentTagsFromDatabase();
+ sheetSet.put(new NodeProperty<>(Bundle.LocalDirectoryNode_createSheet_name_name(),
+ Bundle.LocalDirectoryNode_createSheet_name_displayName(),
+ Bundle.LocalDirectoryNode_createSheet_name_desc(),
+ getName()));
+
+ final String NO_DESCR = Bundle.LocalDirectoryNode_createSheet_noDesc();
+ if(UserPreferences.displayTranslationFileNames()) {
+ String translation = getTranslatedFileName();
+ sheetSet.put(new NodeProperty<>(Bundle.AbstractAbstractFileNode_translateFileName(),
+ Bundle.AbstractAbstractFileNode_translateFileName(), NO_DESCR, translation));
+ }
+
+ addScoreProperty(sheetSet, tags);
+
+ CorrelationAttributeInstance correlationAttribute = null;
+ if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
+ correlationAttribute = getCorrelationAttributeInstance();
+ }
+ addCommentProperty(sheetSet, tags, correlationAttribute);
+
+ if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
+ addCountProperty(sheetSet, correlationAttribute);
+ }
+ // At present, a LocalDirectory will never be a datasource - the top level of a logical
+ // file set is a VirtualDirectory
+ Map map = new LinkedHashMap<>();
+ fillPropertyMap(map, getContent());
+
+ for (Map.Entry entry : map.entrySet()) {
+ sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
+ }
+
+ return sheet;
+ }
+
+ @Override
+>>>>>>> 4316-text-translation-service
public T accept(ContentNodeVisitor visitor) {
return visitor.visit(this);
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
index beb624d1af..5c83708fec 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
@@ -61,7 +61,6 @@ public class LocalFileNode extends AbstractAbstractFileNode {
}
- @Override
public Action[] getActions(boolean context) {
List actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
index 17ee1d078f..17d70834bb 100644
--- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
@@ -170,6 +170,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES:
+ case UserPreferences.DISPLAY_TRANSLATED_NAMES:
case UserPreferences.KEEP_PREFERRED_VIEWER:
refreshContentTreeSafe();
break;
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/NoServiceProviderException.java b/Core/src/org/sleuthkit/autopsy/texttranslation/NoServiceProviderException.java
new file mode 100755
index 0000000000..79590fcfd9
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/NoServiceProviderException.java
@@ -0,0 +1,30 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2018-2018 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.texttranslation;
+
+/**
+ * Exception to indicate that no Service Provider could be found during the
+ * Lookup action.
+ */
+public class NoServiceProviderException extends Exception {
+
+ public NoServiceProviderException(String msg) {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslationService.java b/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslationService.java
index 4f20c748ed..eff494ac8f 100755
--- a/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslationService.java
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslationService.java
@@ -18,27 +18,41 @@
*/
package org.sleuthkit.autopsy.texttranslation;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
import org.openide.util.Lookup;
/**
- * Service for finding and running TextTranslator implementations
+ * Performs a lookup for a TextTranslator service provider and if present,
+ * will use this provider to run translation on the input.
*/
-public class TextTranslationService {
+public final class TextTranslationService {
+ private final static TextTranslationService tts = new TextTranslationService();
+
private final Optional translator;
-
- /**
- *
- */
- public TextTranslationService() {
+
+ private static ExecutorService pool;
+ private final static Integer MAX_POOL_SIZE = 10;
+
+ private TextTranslationService(){
+ //Perform look up for Text Translation implementations ONLY ONCE during
+ //class loading.
translator = Optional.ofNullable(Lookup.getDefault()
.lookup(TextTranslator.class));
}
+
+ public static TextTranslationService getInstance() {
+ return tts;
+ }
/**
- * Performs a lookup for a TextTranslator service provider and if present,
- * will use this provider to run translation on the input.
+ * Translates the input string using whichever TextTranslator Service Provider
+ * was found during lookup.
*
* @param input Input string to be translated
*
@@ -59,13 +73,51 @@ public class TextTranslationService {
}
/**
- * Exception to indicate that no Service Provider could be found during the
- * Lookup action.
+ * Makes the call to translate(String) happen asynchronously on a background
+ * thread. When it is done, it will use the appropriate TranslationCallback
+ * method.
+ *
+ * @param input String to be translated
+ * @param tcb Interface for handling the translation result or any
+ * exceptions thrown while running translate.
+ *
*/
- public class NoServiceProviderException extends Exception {
+ public void translateAsynchronously(String input, TranslationCallback tcb) {
+ if (translator.isPresent()) {
+ //Delay thread pool initialization until an asynchronous task is first called.
+ //That way we don't have threads sitting parked in the background for no reason.
+ if (pool == null) {
+ ThreadFactory translationFactory = new ThreadFactoryBuilder()
+ .setNameFormat("translation-thread-%d")
+ .build();
+ pool = Executors.newFixedThreadPool(MAX_POOL_SIZE, translationFactory);
+ }
- public NoServiceProviderException(String msg) {
- super(msg);
+ //Submit the task to the pool, calling the appropriate method depending
+ //on the result of the translate operation.
+ pool.submit(() -> {
+ try {
+ String translation = translate(input);
+ tcb.onTranslationResult(translation);
+ } catch (NoServiceProviderException ex) {
+ tcb.onNoServiceProviderException(ex);
+ } catch (TranslationException ex) {
+ tcb.onTranslationException(ex);
+ }
+ });
}
+
+ tcb.onNoServiceProviderException(new NoServiceProviderException(
+ "Could not find a TextTranslator service provider"));
+ }
+
+ /**
+ * Returns if a TextTranslator lookup successfully found an implementing
+ * class.
+ *
+ * @return
+ */
+ public boolean hasProvider() {
+ return translator.isPresent();
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslator.java
index e8f8fa94d2..2e6b6bbd07 100755
--- a/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslator.java
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/TextTranslator.java
@@ -24,13 +24,6 @@ package org.sleuthkit.autopsy.texttranslation;
*/
public interface TextTranslator {
- public String translate(String input) throws TranslationException;
+ String translate(String input) throws TranslationException;
- public default String[] translate(String[] inputs) throws TranslationException {
- String[] outputs = new String[inputs.length];
- for(int i = 0; i < inputs.length; i++) {
- outputs[i] = translate(inputs[i]);
- }
- return outputs;
- }
}
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/TranslationCallback.java b/Core/src/org/sleuthkit/autopsy/texttranslation/TranslationCallback.java
new file mode 100755
index 0000000000..8144f6fa1f
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/TranslationCallback.java
@@ -0,0 +1,49 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2018-2018 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.texttranslation;
+
+/**
+ * This callback interface will be used by TextTranslationService when doing
+ * translations on background tasks. When the translation is done, it's result will
+ * be delegated to one of the following methods. It can either be successful or fail with
+ * exceptions.
+ */
+public interface TranslationCallback {
+
+ /**
+ * Provides a method to handle the translation result
+ *
+ * @param translation result of calling TextTranslationService.
+ */
+ void onTranslationResult(String translation);
+
+ /**
+ * Provides a way to handle Translation Exceptions.
+ *
+ * @param noTranslationEx
+ */
+ void onTranslationException(TranslationException noTranslationEx);
+
+ /**
+ * Provides a way to handle NoServiceProviderExceptions.
+ *
+ * @param noServiceEx
+ */
+ void onNoServiceProviderException(NoServiceProviderException noServiceEx);
+}