19 package org.sleuthkit.autopsy.keywordsearch.multicase;
21 import com.google.common.eventbus.Subscribe;
22 import com.google.common.eventbus.DeadEvent;
23 import java.awt.Color;
24 import java.awt.Component;
25 import java.awt.Dimension;
26 import java.io.BufferedWriter;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
39 import java.util.logging.Level;
40 import java.util.stream.Collectors;
41 import javax.swing.AbstractButton;
42 import javax.swing.DefaultListModel;
43 import javax.swing.DefaultListSelectionModel;
44 import javax.swing.JCheckBox;
45 import javax.swing.JFileChooser;
46 import javax.swing.table.TableColumn;
47 import javax.swing.JOptionPane;
48 import javax.swing.JTable;
49 import javax.swing.ListModel;
50 import javax.swing.ListSelectionModel;
51 import javax.swing.SwingUtilities;
52 import javax.swing.filechooser.FileNameExtensionFilter;
53 import javax.swing.table.TableCellRenderer;
54 import org.netbeans.swing.outline.DefaultOutlineModel;
55 import org.openide.explorer.ExplorerManager;
56 import org.netbeans.swing.outline.Outline;
57 import org.openide.nodes.Children;
58 import org.openide.nodes.Node;
59 import org.openide.util.NbBundle.Messages;
60 import org.openide.windows.WindowManager;
70 final class MultiCaseKeywordSearchPanel
extends javax.swing.JPanel implements ExplorerManager.Provider {
73 "MultiCaseKeywordSearchPanel.emptyNode.waitText=Please Wait..."
75 private static final long serialVersionUID = 1L;
76 private volatile SearchThread searchThread = null;
77 private final Outline outline;
78 private final ExplorerManager em;
79 private final org.openide.explorer.view.OutlineView outlineView;
80 private static final Logger LOGGER = Logger.getLogger(MultiCaseKeywordSearchPanel.class.getName());
81 private static final EmptyNode PLEASE_WAIT_NODE =
new EmptyNode(Bundle.MultiCaseKeywordSearchPanel_emptyNode_waitText());
82 private static final MultiCaseKeywordSearchNode NO_RESULTS_NODE =
new MultiCaseKeywordSearchNode(
new ArrayList<>());
83 private Collection<SearchHit> allSearchHits =
new ArrayList<>();
84 private Collection<MultiCaseSearcherException> searchExceptions =
new ArrayList<>();
85 private final SelectMultiUserCasesDialog caseSelectionDialog = SelectMultiUserCasesDialog.getInstance();
86 private final Map<String, CaseNodeData> caseNameToCaseDataMap;
87 private Node[] currentConfirmedSelections;
92 MultiCaseKeywordSearchPanel() {
93 em =
new ExplorerManager();
94 outlineView =
new org.openide.explorer.view.OutlineView();
95 outline = outlineView.getOutline();
96 outlineView.setPropertyColumns(
97 Bundle.MultiCaseKeywordSearchNode_properties_caseDirectory(), Bundle.MultiCaseKeywordSearchNode_properties_caseDirectory(),
98 Bundle.MultiCaseKeywordSearchNode_properties_dataSource(), Bundle.MultiCaseKeywordSearchNode_properties_dataSource(),
99 Bundle.MultiCaseKeywordSearchNode_properties_path(), Bundle.MultiCaseKeywordSearchNode_properties_path(),
100 Bundle.MultiCaseKeywordSearchNode_properties_sourceType(), Bundle.MultiCaseKeywordSearchNode_properties_sourceType(),
101 Bundle.MultiCaseKeywordSearchNode_properties_source(), Bundle.MultiCaseKeywordSearchNode_properties_source());
102 ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.MultiCaseKeywordSearchNode_properties_case());
104 outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
105 outline.setRootVisible(
false);
106 outlineView.setPreferredSize(resultsScrollPane.getPreferredSize());
107 resultsScrollPane.setViewportView(outlineView);
108 caseSelectionDialog.subscribeToNewCaseSelections(
new ChangeListener() {
110 public void nodeSelectionChanged(Node[] selections, List<CaseNodeData> selectionCaseData) {
111 populateCasesList(selectionCaseData);
112 currentConfirmedSelections = selections;
118 outline.setRowSelectionAllowed(
false);
119 searchProgressBar.setVisible(
false);
120 exportButton.setEnabled(
false);
121 outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
122 caseNameToCaseDataMap =
new HashMap<>();
126 caseSelectionList.setSelectionModel(
new DefaultListSelectionModel() {
128 public void setSelectionInterval(
int index0,
int index1) {
129 super.setSelectionInterval(-1, -1);
147 @Messages({
"MultiCaseKeywordSearchPanel.countOfResults.label=Count: "})
149 void subscribeToResults(Collection<SearchHit> hits) {
150 allSearchHits.addAll(hits);
151 if (allSearchHits.size() > 0) {
152 MultiCaseKeywordSearchNode resultsNode =
new MultiCaseKeywordSearchNode(allSearchHits);
153 SwingUtilities.invokeLater(() -> {
154 em.setRootContext(resultsNode);
155 outline.setRowSelectionAllowed(
true);
156 resultsCountLabel.setText(Bundle.MultiCaseKeywordSearchPanel_countOfResults_label() + Integer.toString(outline.getRowCount()));
159 em.setRootContext(NO_RESULTS_NODE);
160 resultsCountLabel.setText(Bundle.MultiCaseKeywordSearchPanel_countOfResults_label() + 0);
172 void subscribeToStrings(String stringReceived) {
173 if (stringReceived.equals(MultiCaseSearcher.getSearchCompleteMessage())) {
174 searchThread.unregisterWithSearcher(MultiCaseKeywordSearchPanel.this);
177 if (!searchExceptions.isEmpty()) {
178 warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_errorsEncounter_text(searchExceptions.size()));
180 if (!em.getRootContext().equals(PLEASE_WAIT_NODE) && !em.getRootContext().equals(NO_RESULTS_NODE)) {
181 exportButton.setEnabled(
true);
182 SwingUtilities.invokeLater(() -> {
183 exportButton.setEnabled(
true);
189 LOGGER.log(Level.INFO,
"String posted to MultiCaseKeywordSearchPanel EventBus with value of " + stringReceived);
200 void subscribeToInterruptionExceptions(InterruptedException exception) {
201 warningLabel.setText(exception.getMessage());
203 if (em.getRootContext().equals(PLEASE_WAIT_NODE)) {
204 em.setRootContext(NO_RESULTS_NODE);
205 resultsCountLabel.setText(Bundle.MultiCaseKeywordSearchPanel_countOfResults_label() + 0);
215 @Messages({
"# {0} - numberOfErrors",
216 "MultiCaseKeywordSearchPanel.errorsEncounter.text={0} Error(s) encountered while performing search"
219 void subscribeToMultiCaseSearcherExceptions(MultiCaseSearcherException exception) {
220 searchExceptions.add(exception);
231 void subscribeToDeadEvents(DeadEvent deadEvent) {
232 LOGGER.log(Level.INFO,
"Dead Event posted to MultiCaseKeywordSearchPanel EventBus " + deadEvent.toString());
235 private void displaySearchErrors() {
236 if (!searchExceptions.isEmpty()) {
237 StringBuilder strBuilder =
new StringBuilder(
"");
238 searchExceptions.forEach((exception) -> {
239 strBuilder.append(
"- ").append(exception.getMessage()).append(System.lineSeparator());
241 SwingUtilities.invokeLater(() -> {
242 new MultiCaseKeywordSearchErrorDialog(strBuilder.toString());
251 private void populateCasesList(List<CaseNodeData> selectedNodes) {
252 caseSelectionList.removeAll();
253 caseSelectionList.revalidate();
254 caseSelectionList.repaint();
255 caseNameToCaseDataMap.clear();
256 DefaultListModel<String> listModel =
new DefaultListModel<>();
257 Collections.sort(selectedNodes, (CaseNodeData o1, CaseNodeData o2) -> {
258 return o1.getName().toLowerCase()
259 .compareTo(o2.getName().toLowerCase());
262 for (
int i = 0; i < selectedNodes.size(); i++) {
263 CaseNodeData data = selectedNodes.get(i);
264 String multiUserCaseName = data.getName();
265 listModel.addElement(multiUserCaseName);
270 caseNameToCaseDataMap.put(multiUserCaseName, data);
272 caseSelectionList.setModel(listModel);
276 public ExplorerManager getExplorerManager() {
285 @SuppressWarnings(
"unchecked")
287 private
void initComponents() {
289 searchTypeGroup =
new javax.swing.ButtonGroup();
290 searchButton =
new javax.swing.JButton();
291 substringRadioButton =
new javax.swing.JRadioButton();
292 keywordTextField =
new javax.swing.JTextField();
293 exactRadioButton =
new javax.swing.JRadioButton();
294 regexRadioButton =
new javax.swing.JRadioButton();
295 casesLabel =
new javax.swing.JLabel();
296 resultsLabel =
new javax.swing.JLabel();
297 toolDescriptionScrollPane =
new javax.swing.JScrollPane();
298 toolDescriptionTextArea =
new javax.swing.JTextArea();
299 resultsScrollPane =
new javax.swing.JScrollPane();
300 cancelButton =
new javax.swing.JButton();
301 searchProgressBar =
new javax.swing.JProgressBar();
302 warningLabel =
new javax.swing.JLabel();
303 exportButton =
new javax.swing.JButton();
304 resultsCountLabel =
new javax.swing.JLabel();
305 viewErrorsButton =
new javax.swing.JButton();
306 pickCasesButton =
new javax.swing.JButton();
307 jScrollPane1 =
new javax.swing.JScrollPane();
308 caseSelectionList =
new javax.swing.JList<>();
312 setPreferredSize(
new java.awt.Dimension(1000, 442));
314 org.openide.awt.Mnemonics.setLocalizedText(searchButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.searchButton.text"));
315 searchButton.setMaximumSize(
new java.awt.Dimension(84, 23));
316 searchButton.setMinimumSize(
new java.awt.Dimension(84, 23));
317 searchButton.setPreferredSize(
new java.awt.Dimension(84, 23));
318 searchButton.addActionListener(
new java.awt.event.ActionListener() {
319 public void actionPerformed(java.awt.event.ActionEvent evt) {
320 searchButtonActionPerformed(evt);
324 searchTypeGroup.add(substringRadioButton);
325 org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.substringRadioButton.text_1"));
326 substringRadioButton.addActionListener(
new java.awt.event.ActionListener() {
327 public void actionPerformed(java.awt.event.ActionEvent evt) {
328 substringRadioButtonActionPerformed(evt);
332 keywordTextField.setFont(
new java.awt.Font(
"Monospaced", 0, 14));
333 keywordTextField.setText(
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.keywordTextField.text_1"));
334 keywordTextField.setBorder(
new javax.swing.border.LineBorder(
new java.awt.Color(192, 192, 192), 1,
true));
335 keywordTextField.setMinimumSize(
new java.awt.Dimension(2, 25));
336 keywordTextField.setPreferredSize(
new java.awt.Dimension(2, 25));
338 searchTypeGroup.add(exactRadioButton);
339 exactRadioButton.setSelected(
true);
340 org.openide.awt.Mnemonics.setLocalizedText(exactRadioButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.exactRadioButton.text_1"));
342 searchTypeGroup.add(regexRadioButton);
343 org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.regexRadioButton.text_1"));
345 org.openide.awt.Mnemonics.setLocalizedText(casesLabel,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.casesLabel.text_1"));
347 org.openide.awt.Mnemonics.setLocalizedText(resultsLabel,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.resultsLabel.text"));
349 toolDescriptionTextArea.setEditable(
false);
350 toolDescriptionTextArea.setBackground(
new java.awt.Color(240, 240, 240));
351 toolDescriptionTextArea.setColumns(20);
352 toolDescriptionTextArea.setFont(
new java.awt.Font(
"Tahoma", 0, 11));
353 toolDescriptionTextArea.setLineWrap(
true);
354 toolDescriptionTextArea.setRows(3);
355 toolDescriptionTextArea.setText(
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.toolDescriptionTextArea.text"));
356 toolDescriptionTextArea.setWrapStyleWord(
true);
357 toolDescriptionTextArea.setFocusable(
false);
358 toolDescriptionScrollPane.setViewportView(toolDescriptionTextArea);
360 resultsScrollPane.setMinimumSize(
new java.awt.Dimension(100, 40));
361 resultsScrollPane.setPreferredSize(
new java.awt.Dimension(200, 100));
362 resultsScrollPane.setRequestFocusEnabled(
false);
364 org.openide.awt.Mnemonics.setLocalizedText(cancelButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.cancelButton.text"));
365 cancelButton.setEnabled(
false);
366 cancelButton.addActionListener(
new java.awt.event.ActionListener() {
367 public void actionPerformed(java.awt.event.ActionEvent evt) {
368 cancelButtonActionPerformed(evt);
372 warningLabel.setForeground(
new java.awt.Color(200, 0, 0));
373 org.openide.awt.Mnemonics.setLocalizedText(warningLabel,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.warningLabel.text"));
374 warningLabel.setFocusable(
false);
376 org.openide.awt.Mnemonics.setLocalizedText(exportButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.exportButton.text"));
377 exportButton.setMargin(
new java.awt.Insets(2, 2, 2, 2));
378 exportButton.setMaximumSize(
new java.awt.Dimension(84, 23));
379 exportButton.setMinimumSize(
new java.awt.Dimension(84, 23));
380 exportButton.setPreferredSize(
new java.awt.Dimension(84, 23));
381 exportButton.addActionListener(
new java.awt.event.ActionListener() {
382 public void actionPerformed(java.awt.event.ActionEvent evt) {
383 exportButtonActionPerformed(evt);
387 resultsCountLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
388 org.openide.awt.Mnemonics.setLocalizedText(resultsCountLabel,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.resultsCountLabel.text"));
390 org.openide.awt.Mnemonics.setLocalizedText(viewErrorsButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.viewErrorsButton.text"));
391 viewErrorsButton.setEnabled(
false);
392 viewErrorsButton.addActionListener(
new java.awt.event.ActionListener() {
393 public void actionPerformed(java.awt.event.ActionEvent evt) {
394 viewErrorsButtonActionPerformed(evt);
398 org.openide.awt.Mnemonics.setLocalizedText(pickCasesButton,
org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class,
"MultiCaseKeywordSearchPanel.pickCasesButton.text_1"));
399 pickCasesButton.addActionListener(
new java.awt.event.ActionListener() {
400 public void actionPerformed(java.awt.event.ActionEvent evt) {
401 pickCasesButtonActionPerformed(evt);
405 jScrollPane1.setViewportView(caseSelectionList);
407 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
408 this.setLayout(layout);
409 layout.setHorizontalGroup(
410 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
411 .addGroup(layout.createSequentialGroup()
413 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
414 .addGroup(layout.createSequentialGroup()
415 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
416 .addGroup(layout.createSequentialGroup()
417 .addComponent(exactRadioButton)
418 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
419 .addComponent(substringRadioButton)
420 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
421 .addComponent(regexRadioButton))
422 .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 591, Short.MAX_VALUE))
423 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
424 .addComponent(toolDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE))
425 .addGroup(layout.createSequentialGroup()
426 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
427 .addComponent(casesLabel)
428 .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE)
429 .addGroup(layout.createSequentialGroup()
430 .addComponent(pickCasesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)
431 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
432 .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
433 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
434 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
435 .addGroup(layout.createSequentialGroup()
436 .addComponent(resultsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE)
437 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
438 .addComponent(resultsCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE))
439 .addComponent(resultsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
440 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
441 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
442 .addComponent(viewErrorsButton)
443 .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 607, Short.MAX_VALUE))
445 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
446 .addComponent(exportButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
447 .addComponent(cancelButton, javax.swing.GroupLayout.DEFAULT_SIZE, 87, Short.MAX_VALUE))))))
449 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
450 .addGroup(layout.createSequentialGroup()
451 .addGap(196, 196, 196)
452 .addComponent(searchProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 608, Short.MAX_VALUE)
453 .addGap(108, 108, 108)))
455 layout.setVerticalGroup(
456 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
457 .addGroup(layout.createSequentialGroup()
459 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
460 .addGroup(layout.createSequentialGroup()
461 .addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
462 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
463 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
464 .addComponent(regexRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
465 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
466 .addComponent(exactRadioButton)
467 .addComponent(substringRadioButton))))
468 .addComponent(toolDescriptionScrollPane))
469 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
470 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
471 .addComponent(casesLabel)
472 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
473 .addComponent(resultsLabel)
474 .addComponent(resultsCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)))
475 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
476 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
477 .addComponent(resultsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE)
478 .addComponent(jScrollPane1))
479 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
480 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
481 .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
482 .addComponent(exportButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
483 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
484 .addComponent(pickCasesButton)
485 .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
486 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
487 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
488 .addComponent(viewErrorsButton)
489 .addComponent(cancelButton))
491 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
492 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
493 .addContainerGap(433, Short.MAX_VALUE)
494 .addComponent(searchProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
500 "MultiCaseKeywordSearchPanel.warningText.noCases=At least one case must be selected to perform a search.",
501 "MultiCaseKeywordSearchPanel.warningText.emptySearch=You must enter something to search for in the text field."
507 private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {
508 if (null == searchThread) {
509 Collection<String> cases = getCases();
510 String searchString = keywordTextField.getText();
511 if (cases.isEmpty()) {
512 warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_warningText_noCases());
513 }
else if (searchString.isEmpty()) {
514 warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_warningText_emptySearch());
517 Collection<CaseNodeData> caseNodeData = cases.stream()
518 .map(c -> caseNameToCaseDataMap.get(c))
519 .collect(Collectors.toList());
522 warningLabel.setText(
"");
523 allSearchHits =
new ArrayList<>();
524 searchExceptions =
new ArrayList<>();
525 searchEnabled(
false);
526 exportButton.setEnabled(
false);
527 outline.setRowSelectionAllowed(
false);
528 SearchQuery kwsQuery =
new SearchQuery(getQueryType(), searchString);
529 em.setRootContext(PLEASE_WAIT_NODE);
530 resultsCountLabel.setText(
"");
531 searchThread =
new SearchThread(caseNodeData, kwsQuery);
532 searchThread.registerWithSearcher(MultiCaseKeywordSearchPanel.this);
533 searchThread.start();
543 private Collection<String> getCases() {
544 Collection<String> cases =
new HashSet<>();
545 ListModel<String> listModel = caseSelectionList.getModel();
546 for(
int i = 0; i < listModel.getSize(); i++) {
547 String caseName = listModel.getElementAt(i);
558 private QueryType getQueryType() {
559 String queryTypeText =
"";
560 Enumeration<AbstractButton> buttonGroup = searchTypeGroup.getElements();
561 while (buttonGroup.hasMoreElements()) {
562 AbstractButton dspButton = buttonGroup.nextElement();
563 if (dspButton.isSelected()) {
564 queryTypeText = dspButton.getText();
568 if (queryTypeText.equals(substringRadioButton.getText())) {
569 return QueryType.SUBSTRING;
570 }
else if (queryTypeText.equals(regexRadioButton.getText())) {
571 return QueryType.REGEX;
574 return QueryType.EXACT_MATCH;
582 private void setColumnWidths() {
583 int widthLimit = 1000;
586 for (
int col = 0; col < outline.getColumnCount(); col++) {
588 int rowsToResize = Math.min(outline.getRowCount(), 100);
589 for (
int row = 0; row < rowsToResize; row++) {
590 if (outline.getValueAt(row, col) != null) {
591 TableCellRenderer renderer = outline.getCellRenderer(row, col);
592 Component comp = outline.prepareRenderer(renderer, row, col);
593 width = Math.max(comp.getPreferredSize().width, width);
597 width += 2 * margin + padding;
598 width = Math.min(width, widthLimit);
599 TableColumn column = outline.getColumnModel().getColumn(outline.convertColumnIndexToModel(col));
600 column.setPreferredWidth(width);
602 resultsScrollPane.setPreferredSize(
new Dimension(outline.getPreferredSize().width, resultsScrollPane.getPreferredSize().height));
610 private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {
618 "MultiCaseKeywordSearchPanel.searchThread.cancellingText=Cancelling search"})
619 private void cancelSearch() {
620 if (null != searchThread) {
621 warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_searchThread_cancellingText());
622 searchThread.interrupt();
626 @Messages({
"MultiCaseKeywordSearchPanel.searchResultsExport.csvExtensionFilterlbl=Comma Separated Values File (csv)",
627 "MultiCaseKeywordSearchPanel.searchResultsExport.featureName=Search Results Export",
628 "MultiCaseKeywordSearchPanel.searchResultsExport.failedExportMsg=Export of search results failed"
634 private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {
635 JFileChooser chooser =
new JFileChooser();
636 final String EXTENSION =
"csv";
637 FileNameExtensionFilter csvFilter =
new FileNameExtensionFilter(
638 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_csvExtensionFilterlbl(), EXTENSION);
639 chooser.setFileFilter(csvFilter);
640 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
641 chooser.setName(
"Choose file to export results to");
642 chooser.setMultiSelectionEnabled(
false);
643 int returnVal = chooser.showSaveDialog(
this);
644 if (returnVal == JFileChooser.APPROVE_OPTION) {
645 File selFile = chooser.getSelectedFile();
646 if (selFile == null) {
647 JOptionPane.showMessageDialog(
this,
648 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_failedExportMsg(),
649 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_featureName(),
650 JOptionPane.WARNING_MESSAGE);
651 LOGGER.warning(
"Selected file was null, when trying to export search results");
654 String fileAbs = selFile.getAbsolutePath();
655 if (!fileAbs.endsWith(
"." + EXTENSION)) {
656 fileAbs = fileAbs +
"." + EXTENSION;
657 selFile =
new File(fileAbs);
659 saveResultsAsTextFile(selFile);
663 private void viewErrorsButtonActionPerformed(java.awt.event.ActionEvent evt) {
664 displaySearchErrors();
667 private void pickCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {
668 caseSelectionDialog.setVisible(
true);
669 if (currentConfirmedSelections != null) {
670 caseSelectionDialog.setNodeSelections(currentConfirmedSelections);
675 private void substringRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
686 private void searchEnabled(
boolean canSearch) {
687 searchButton.setEnabled(canSearch);
688 cancelButton.setEnabled(!canSearch);
689 viewErrorsButton.setEnabled(canSearch);
690 viewErrorsButton.setVisible(!searchExceptions.isEmpty());
693 @Messages({
"# {0} - file name",
694 "MultiCaseKeywordSearchPanel.searchResultsExport.fileExistPrompt=File {0} exists, overwrite?",
696 "MultiCaseKeywordSearchPanel.searchResultsExport.exportMsg=Search results exported to {0}"
701 private void saveResultsAsTextFile(File resultsFile) {
702 if (resultsFile.exists()) {
704 boolean shouldWrite = JOptionPane.showConfirmDialog(null,
705 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_fileExistPrompt(resultsFile.getName()),
706 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_featureName(),
707 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE)
708 == JOptionPane.YES_OPTION;
714 BufferedWriter resultsWriter;
715 resultsWriter =
new BufferedWriter(
new FileWriter(resultsFile));
718 while (col < outline.getColumnCount()) {
720 resultsWriter.write(outline.getColumnName(col));
722 if (col < outline.getColumnCount()) {
723 resultsWriter.write(
",");
726 resultsWriter.write(System.lineSeparator());
728 Children resultsChildren = em.getRootContext().getChildren();
729 for (
int row = 0; row < resultsChildren.getNodesCount(); row++) {
731 while (col < outline.getColumnCount()) {
732 if (outline.getValueAt(row, col) instanceof Node.Property) {
733 resultsWriter.write(((Node.Property) outline.getValueAt(row, col)).getValue().toString());
735 resultsWriter.write(outline.getValueAt(row, col).toString());
738 if (col < outline.getColumnCount()) {
739 resultsWriter.write(
",");
742 resultsWriter.write(System.lineSeparator());
744 resultsWriter.flush();
745 resultsWriter.close();
747 JOptionPane.showMessageDialog(
748 WindowManager.getDefault().getMainWindow(),
749 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_exportMsg(resultsFile.getName()),
750 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_featureName(),
751 JOptionPane.INFORMATION_MESSAGE);
752 }
catch (IllegalAccessException | IOException | InvocationTargetException ex) {
753 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
754 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_failedExportMsg(),
755 Bundle.MultiCaseKeywordSearchPanel_searchResultsExport_featureName(),
756 JOptionPane.WARNING_MESSAGE);
757 LOGGER.log(Level.WARNING,
"Export of search results failed unable to write results csv file", ex);
766 "MultiCaseKeywordSearchPanel.continueSearch.text=A search is currently being performed. "
767 +
"Would you like the search to continue in the background while the search window is closed?",
768 "MultiCaseKeywordSearchPanel.continueSearch.title=Closing multi-case search"
770 void closeSearchPanel() {
771 if (cancelButton.isEnabled()) {
772 boolean shouldContinueSearch = JOptionPane.showConfirmDialog(null,
773 Bundle.MultiCaseKeywordSearchPanel_continueSearch_text(),
774 Bundle.MultiCaseKeywordSearchPanel_continueSearch_title(),
775 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE)
776 == JOptionPane.YES_OPTION;
777 if (!shouldContinueSearch) {
784 private javax.swing.JButton cancelButton;
785 private javax.swing.JList<String> caseSelectionList;
786 private javax.swing.JLabel casesLabel;
787 private javax.swing.JRadioButton exactRadioButton;
788 private javax.swing.JButton exportButton;
789 private javax.swing.JScrollPane jScrollPane1;
790 private javax.swing.JTextField keywordTextField;
791 private javax.swing.JButton pickCasesButton;
792 private javax.swing.JRadioButton regexRadioButton;
793 private javax.swing.JLabel resultsCountLabel;
794 private javax.swing.JLabel resultsLabel;
795 private javax.swing.JScrollPane resultsScrollPane;
796 private javax.swing.JButton searchButton;
797 private javax.swing.JProgressBar searchProgressBar;
798 private javax.swing.ButtonGroup searchTypeGroup;
799 private javax.swing.JRadioButton substringRadioButton;
800 private javax.swing.JScrollPane toolDescriptionScrollPane;
801 private javax.swing.JTextArea toolDescriptionTextArea;
802 private javax.swing.JButton viewErrorsButton;
803 private javax.swing.JLabel warningLabel;
821 private SearchThread(Collection<CaseNodeData> caseNodes, SearchQuery searchQuery) {
833 multiCaseSearcher.registerWithEventBus(
object);
843 multiCaseSearcher.unregisterWithEventBus(
object);
850 multiCaseSearcher.stopMultiCaseSearch();
855 multiCaseSearcher.performKeywordSearch(caseNodes, searchQuery,
new MultiCaseKeywordSearchProgressIndicator(searchProgressBar));
SearchThread(Collection< CaseNodeData > caseNodes, SearchQuery searchQuery)
final SearchQuery searchQuery
void registerWithSearcher(Object object)
final MultiCaseSearcher multiCaseSearcher
final Collection< CaseNodeData > caseNodes
void nodeSelectionChanged(Node[] selections, List< CaseNodeData > selectionCaseData)
void unregisterWithSearcher(Object object)