Autopsy  4.17.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
TranslatedTextViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.texttranslation.ui;
20 
21 import com.google.common.collect.Lists;
22 import com.google.common.util.concurrent.ThreadFactoryBuilder;
23 import java.awt.Component;
24 import java.awt.ComponentOrientation;
25 import java.awt.Font;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.io.IOException;
29 import java.io.Reader;
30 import java.util.Arrays;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.ThreadFactory;
34 import org.openide.nodes.Node;
35 import org.openide.util.lookup.ServiceProvider;
37 import org.sleuthkit.datamodel.AbstractFile;
38 import org.openide.util.Lookup;
39 import org.openide.util.NbBundle;
40 import org.openide.util.lookup.Lookups;
46 import java.util.List;
47 import java.util.logging.Level;
48 import javax.swing.SwingUtilities;
53 
57 @ServiceProvider(service = TextViewer.class, position = 4)
58 public final class TranslatedTextViewer implements TextViewer {
59 
60  private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName());
61 
62  private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
63  private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
64  private final TranslationContentPanel panel = new TranslationContentPanel();
65 
66  private volatile Node node;
68  private final ThreadFactory translationThreadFactory
69  = new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build();
70  private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
71 
72  @NbBundle.Messages({
73  "TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
74  })
75  @Override
76  public void setNode(final Node node) {
77  this.node = node;
78  SelectionChangeListener displayDropDownListener = new DisplayDropDownChangeListener();
79  panel.addDisplayTextActionListener(displayDropDownListener);
80  panel.addOcrDropDownActionListener(new OCRDropdownChangeListener());
82  panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
83  }
84  panel.enableOCRSelection(UserPreferences.getUseOcrInTranslation());
85 
86  int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000;
87  panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
88 
89  //Force a background task.
90  displayDropDownListener.actionPerformed(null);
91  }
92 
93  @NbBundle.Messages({"TranslatedTextViewer.title=Translation"})
94  @Override
95  public String getTitle() {
96  return Bundle.TranslatedTextViewer_title();
97  }
98 
99  @NbBundle.Messages({"TranslatedTextViewer.toolTip=Displays translated file text."})
100  @Override
101  public String getToolTip() {
102  return Bundle.TranslatedTextViewer_toolTip();
103  }
104 
105  @Override
106  public TextViewer createInstance() {
107  return new TranslatedTextViewer();
108  }
109 
110  @Override
111  public Component getComponent() {
112  return panel;
113  }
114 
115  @Override
116  public void resetComponent() {
117  panel.reset();
118  this.node = null;
119  if (backgroundTask != null) {
120  backgroundTask.cancel(true);
121  }
122  backgroundTask = null;
123  }
124 
125  @Override
126  public boolean isSupported(Node node) {
127  if (null == node) {
128  return false;
129  }
130 
132  return false;
133  }
134 
135  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
136  return file != null;
137  }
138 
139  @Override
140  public int isPreferred(Node node) {
141  // Returning zero makes it unlikely this object will be the preferred content viewer,
142  // i.e., the active tab, when the content viewers are first displayed.
143  return 0;
144  }
145 
150 
151  private final AbstractFile file;
152 
153  private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) {
154  super(translateText, String.format("%s (objId=%d)", file.getName(), file.getId()));
155  this.file = file;
156  }
157 
165  @NbBundle.Messages({
166  "TranslatedContentViewer.extractingText=Extracting text, please wait...",
167  "# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).",
168  })
169  protected String retrieveText() throws IOException, InterruptedException, IllegalStateException {
170  SwingUtilities.invokeLater(() -> {
171  onProgressDisplay(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
172  });
173 
174  try {
175  return getFileText(file);
176  } catch (IOException | TextExtractor.InitReaderException ex) {
177  logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex);
178  // throw new exception with message to be displayed to user
179  throw new IllegalStateException(Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage()), ex);
180  }
181  }
182 
183 
196  @NbBundle.Messages({
197  "TranslatedContentViewer.ocrNotEnabled=OCR is not enabled. To change, go to Tools->Options->Machine Translation",
198  })
199  private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException {
200  final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS
201  if (isImage && ! UserPreferences.getUseOcrInTranslation()) {
202  return Bundle.TranslatedContentViewer_ocrNotEnabled();
203  }
204 
205  String result = extractText(file, UserPreferences.getUseOcrInTranslation());
206 
207  //Correct for UTF-8
208  byte[] resultInUTF8Bytes = result.getBytes("UTF8");
209  byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
210  Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES));
211  return new String(trimToArraySize, "UTF-8");
212  }
213 
227  private String extractText(AbstractFile source, boolean ocrEnabled) throws IOException, InterruptedException, TextExtractor.InitReaderException {
228  Reader textExtractor = getTextExtractor(source, ocrEnabled);
229 
230  char[] cbuf = new char[8096];
231  StringBuilder textBuilder = new StringBuilder();
232 
233  //bytesRead can be an int so long as the max file size
234  //is sufficiently small
235  int bytesRead = 0;
236  int read;
237 
238  while ((read = textExtractor.read(cbuf)) != -1) {
239  if (this.isCancelled()) {
240  throw new InterruptedException();
241  }
242 
243  //Short-circuit the read if its greater than our max
244  //translatable size
245  int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
246 
247  if (bytesLeft < read) {
248  textBuilder.append(cbuf, 0, bytesLeft);
249  return textBuilder.toString();
250  }
251 
252  textBuilder.append(cbuf, 0, read);
253  bytesRead += read;
254  }
255 
256  return textBuilder.toString();
257  }
258 
272  private Reader getTextExtractor(AbstractFile file, boolean ocrEnabled) throws IOException,
274  Lookup context = null;
275 
276  if (ocrEnabled) {
277  ImageConfig imageConfig = new ImageConfig();
278  imageConfig.setOCREnabled(true);
279 
280  String ocrSelection = panel.getSelectedOcrLanguagePack();
281  if (!ocrSelection.isEmpty()) {
282  imageConfig.setOCRLanguages(Lists.newArrayList(ocrSelection));
283  }
284 
285  //Terminate any OS process running in the extractor if this
286  //SwingWorker has been cancelled.
287  ProcessTerminator terminator = () -> isCancelled();
288  context = Lookups.fixed(imageConfig, terminator);
289  }
290 
291  try {
292  return TextExtractorFactory.getExtractor(file, context).getReader();
294  //Fall-back onto the strings extractor
295  return TextExtractorFactory.getStringsExtractor(file, context).getReader();
296  }
297  }
298 
299 
300  @Override
301  protected void onTextDisplay(String text, ComponentOrientation orientation, int font) {
302  panel.display(text, orientation, font);
303  }
304  }
305 
306 
311  private abstract class SelectionChangeListener implements ActionListener {
312 
313  private String currentSelection;
314 
315  abstract String getSelection();
316 
317  @Override
318  public final void actionPerformed(ActionEvent e) {
319  String selection = getSelection();
320  if (!selection.equals(currentSelection)) {
321  currentSelection = selection;
322 
323  if (backgroundTask != null && !backgroundTask.isDone()) {
324  backgroundTask.cancel(true);
325  }
326 
327  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
328  String textDisplaySelection = panel.getDisplayDropDownSelection();
329  boolean translateText = !textDisplaySelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString());
330  backgroundTask = new ExtractAndTranslateTextTask(file, translateText);
331 
332  //Pass the background task to a single threaded pool to keep
333  //the number of jobs running to one.
334  executorService.execute(backgroundTask);
335  }
336  }
337  }
338 
343 
344  @Override
345  String getSelection() {
346  return panel.getDisplayDropDownSelection();
347  }
348  }
349 
354 
355  @Override
356  String getSelection() {
357  return panel.getSelectedOcrLanguagePack();
358  }
359  }
360 }
void display(String text, ComponentOrientation direction, int font)
static TextExtractor getStringsExtractor(Content content, Lookup context)
static TextExtractor getExtractor(Content content, Lookup context)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.