Autopsy  4.10.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
MultiCaseKeywordSearchPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 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.keywordsearch.multicase;
20 
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;
27 import java.io.File;
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;
38 import java.util.Map;
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;
66 
70 final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
71 
72  @Messages({
73  "MultiCaseKeywordSearchPanel.emptyNode.waitText=Please Wait..."
74  })
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;
88 
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());
103  initComponents();
104  outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
105  outline.setRootVisible(false);
106  outlineView.setPreferredSize(resultsScrollPane.getPreferredSize());
107  resultsScrollPane.setViewportView(outlineView);
108  caseSelectionDialog.subscribeToNewCaseSelections(new ChangeListener() {
109  @Override
110  public void nodeSelectionChanged(Node[] selections, List<CaseNodeData> selectionCaseData) {
111  populateCasesList(selectionCaseData);
112  currentConfirmedSelections = selections;
113  revalidate();
114  repaint();
115  }
116  });
117  searchEnabled(true);
118  outline.setRowSelectionAllowed(false);
119  searchProgressBar.setVisible(false);
120  exportButton.setEnabled(false);
121  outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
122  caseNameToCaseDataMap = new HashMap<>();
123  setColumnWidths();
124 
125  //Disable selection in JList
126  caseSelectionList.setSelectionModel(new DefaultListSelectionModel() {
127  @Override
128  public void setSelectionInterval(int index0, int index1) {
129  super.setSelectionInterval(-1, -1);
130  }
131  });
132  }
133 
137  public interface ChangeListener {
138  public void nodeSelectionChanged(Node[] selections, List<CaseNodeData> selectionCaseData);
139  }
140 
147  @Messages({"MultiCaseKeywordSearchPanel.countOfResults.label=Count: "})
148  @Subscribe
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()));
157  });
158  } else {
159  em.setRootContext(NO_RESULTS_NODE);
160  resultsCountLabel.setText(Bundle.MultiCaseKeywordSearchPanel_countOfResults_label() + 0);
161  }
162  }
163 
171  @Subscribe
172  void subscribeToStrings(String stringReceived) {
173  if (stringReceived.equals(MultiCaseSearcher.getSearchCompleteMessage())) {
174  searchThread.unregisterWithSearcher(MultiCaseKeywordSearchPanel.this);
175  searchThread = null;
176  searchEnabled(true);
177  if (!searchExceptions.isEmpty()) {
178  warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_errorsEncounter_text(searchExceptions.size()));
179  }
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);
184  setColumnWidths();
185  });
186  }
187  } else {
188  //If it is not the SEARCH_COMPLETE_STRING log it.
189  LOGGER.log(Level.INFO, "String posted to MultiCaseKeywordSearchPanel EventBus with value of " + stringReceived);
190  }
191  }
192 
199  @Subscribe
200  void subscribeToInterruptionExceptions(InterruptedException exception) {
201  warningLabel.setText(exception.getMessage());
202  //if we are still displaying please wait force it to update to no results
203  if (em.getRootContext().equals(PLEASE_WAIT_NODE)) {
204  em.setRootContext(NO_RESULTS_NODE);
205  resultsCountLabel.setText(Bundle.MultiCaseKeywordSearchPanel_countOfResults_label() + 0);
206  }
207  }
208 
215  @Messages({"# {0} - numberOfErrors",
216  "MultiCaseKeywordSearchPanel.errorsEncounter.text={0} Error(s) encountered while performing search"
217  })
218  @Subscribe
219  void subscribeToMultiCaseSearcherExceptions(MultiCaseSearcherException exception) {
220  searchExceptions.add(exception);
221  }
222 
230  @Subscribe
231  void subscribeToDeadEvents(DeadEvent deadEvent) {
232  LOGGER.log(Level.INFO, "Dead Event posted to MultiCaseKeywordSearchPanel EventBus " + deadEvent.toString());
233  }
234 
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());
240  });
241  SwingUtilities.invokeLater(() -> {
242  new MultiCaseKeywordSearchErrorDialog(strBuilder.toString());
243  });
244  }
245 
246  }
247 
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());
260  });
261 
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);
271  }
272  caseSelectionList.setModel(listModel);
273  }
274 
275  @Override
276  public ExplorerManager getExplorerManager() {
277  return em;
278  }
279 
285  @SuppressWarnings("unchecked")
286  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
287  private void initComponents() {
288 
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<>();
309 
310  setName(""); // NOI18N
311  setOpaque(false);
312  setPreferredSize(new java.awt.Dimension(1000, 442));
313 
314  org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.searchButton.text")); // NOI18N
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);
321  }
322  });
323 
324  searchTypeGroup.add(substringRadioButton);
325  org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.substringRadioButton.text_1")); // NOI18N
326  substringRadioButton.addActionListener(new java.awt.event.ActionListener() {
327  public void actionPerformed(java.awt.event.ActionEvent evt) {
328  substringRadioButtonActionPerformed(evt);
329  }
330  });
331 
332  keywordTextField.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N
333  keywordTextField.setText(org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.keywordTextField.text_1")); // NOI18N
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));
337 
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")); // NOI18N
341 
342  searchTypeGroup.add(regexRadioButton);
343  org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.regexRadioButton.text_1")); // NOI18N
344 
345  org.openide.awt.Mnemonics.setLocalizedText(casesLabel, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.casesLabel.text_1")); // NOI18N
346 
347  org.openide.awt.Mnemonics.setLocalizedText(resultsLabel, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.resultsLabel.text")); // NOI18N
348 
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)); // NOI18N
353  toolDescriptionTextArea.setLineWrap(true);
354  toolDescriptionTextArea.setRows(3);
355  toolDescriptionTextArea.setText(org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.toolDescriptionTextArea.text")); // NOI18N
356  toolDescriptionTextArea.setWrapStyleWord(true);
357  toolDescriptionTextArea.setFocusable(false);
358  toolDescriptionScrollPane.setViewportView(toolDescriptionTextArea);
359 
360  resultsScrollPane.setMinimumSize(new java.awt.Dimension(100, 40));
361  resultsScrollPane.setPreferredSize(new java.awt.Dimension(200, 100));
362  resultsScrollPane.setRequestFocusEnabled(false);
363 
364  org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.cancelButton.text")); // NOI18N
365  cancelButton.setEnabled(false);
366  cancelButton.addActionListener(new java.awt.event.ActionListener() {
367  public void actionPerformed(java.awt.event.ActionEvent evt) {
368  cancelButtonActionPerformed(evt);
369  }
370  });
371 
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")); // NOI18N
374  warningLabel.setFocusable(false);
375 
376  org.openide.awt.Mnemonics.setLocalizedText(exportButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.exportButton.text")); // NOI18N
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);
384  }
385  });
386 
387  resultsCountLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
388  org.openide.awt.Mnemonics.setLocalizedText(resultsCountLabel, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.resultsCountLabel.text")); // NOI18N
389 
390  org.openide.awt.Mnemonics.setLocalizedText(viewErrorsButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.viewErrorsButton.text")); // NOI18N
391  viewErrorsButton.setEnabled(false);
392  viewErrorsButton.addActionListener(new java.awt.event.ActionListener() {
393  public void actionPerformed(java.awt.event.ActionEvent evt) {
394  viewErrorsButtonActionPerformed(evt);
395  }
396  });
397 
398  org.openide.awt.Mnemonics.setLocalizedText(pickCasesButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.pickCasesButton.text_1")); // NOI18N
399  pickCasesButton.addActionListener(new java.awt.event.ActionListener() {
400  public void actionPerformed(java.awt.event.ActionEvent evt) {
401  pickCasesButtonActionPerformed(evt);
402  }
403  });
404 
405  jScrollPane1.setViewportView(caseSelectionList);
406 
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()
412  .addContainerGap()
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))
444  .addGap(14, 14, 14)
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))))))
448  .addContainerGap())
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)))
454  );
455  layout.setVerticalGroup(
456  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
457  .addGroup(layout.createSequentialGroup()
458  .addContainerGap()
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))
490  .addContainerGap())
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)
495  .addContainerGap()))
496  );
497  }// </editor-fold>//GEN-END:initComponents
498 
499  @Messages({
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."
502  })
507  private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
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());
515  } else {
516  //Map case names to CaseNodeData objects
517  Collection<CaseNodeData> caseNodeData = cases.stream()
518  .map(c -> caseNameToCaseDataMap.get(c))
519  .collect(Collectors.toList());
520 
521  //perform the search
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();
534  }
535  }
536  }//GEN-LAST:event_searchButtonActionPerformed
537 
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);
548  cases.add(caseName);
549  }
550  return cases;
551  }
552 
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();
565  break;
566  }
567  }
568  if (queryTypeText.equals(substringRadioButton.getText())) {
569  return QueryType.SUBSTRING;
570  } else if (queryTypeText.equals(regexRadioButton.getText())) {
571  return QueryType.REGEX;
572  } else {
573  //default to Exact match
574  return QueryType.EXACT_MATCH;
575  }
576  }
577 
582  private void setColumnWidths() {
583  int widthLimit = 1000;
584  int margin = 4;
585  int padding = 8;
586  for (int col = 0; col < outline.getColumnCount(); col++) {
587  int width = 115; //min initial width for columns
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);
594  }
595 
596  }
597  width += 2 * margin + padding;
598  width = Math.min(width, widthLimit);
599  TableColumn column = outline.getColumnModel().getColumn(outline.convertColumnIndexToModel(col));
600  column.setPreferredWidth(width);
601  }
602  resultsScrollPane.setPreferredSize(new Dimension(outline.getPreferredSize().width, resultsScrollPane.getPreferredSize().height));
603  }
604 
610  private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
611  cancelSearch();
612  }//GEN-LAST:event_cancelButtonActionPerformed
613 
617  @Messages({
618  "MultiCaseKeywordSearchPanel.searchThread.cancellingText=Cancelling search"})
619  private void cancelSearch() {
620  if (null != searchThread) {
621  warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_searchThread_cancellingText());
622  searchThread.interrupt();
623  }
624  }
625 
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"
629  })
634  private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed
635  JFileChooser chooser = new JFileChooser();
636  final String EXTENSION = "csv"; //NON-NLS
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");
652  return;
653  }
654  String fileAbs = selFile.getAbsolutePath();
655  if (!fileAbs.endsWith("." + EXTENSION)) {
656  fileAbs = fileAbs + "." + EXTENSION;
657  selFile = new File(fileAbs);
658  }
659  saveResultsAsTextFile(selFile);
660  }
661  }//GEN-LAST:event_exportButtonActionPerformed
662 
663  private void viewErrorsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewErrorsButtonActionPerformed
664  displaySearchErrors();
665  }//GEN-LAST:event_viewErrorsButtonActionPerformed
666 
667  private void pickCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickCasesButtonActionPerformed
668  caseSelectionDialog.setVisible(true);
669  if (currentConfirmedSelections != null) {
670  caseSelectionDialog.setNodeSelections(currentConfirmedSelections);
671  }
672 
673  }//GEN-LAST:event_pickCasesButtonActionPerformed
674 
675  private void substringRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_substringRadioButtonActionPerformed
676  // TODO add your handling code here:
677  }//GEN-LAST:event_substringRadioButtonActionPerformed
678 
686  private void searchEnabled(boolean canSearch) {
687  searchButton.setEnabled(canSearch);
688  cancelButton.setEnabled(!canSearch);
689  viewErrorsButton.setEnabled(canSearch);
690  viewErrorsButton.setVisible(!searchExceptions.isEmpty());
691  }
692 
693  @Messages({"# {0} - file name",
694  "MultiCaseKeywordSearchPanel.searchResultsExport.fileExistPrompt=File {0} exists, overwrite?",
695  "# {0} - file name",
696  "MultiCaseKeywordSearchPanel.searchResultsExport.exportMsg=Search results exported to {0}"
697  })
701  private void saveResultsAsTextFile(File resultsFile) {
702  if (resultsFile.exists()) {
703  //if the file already exists ask the user how to proceed
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;
709  if (!shouldWrite) {
710  return;
711  }
712  }
713  try {
714  BufferedWriter resultsWriter;
715  resultsWriter = new BufferedWriter(new FileWriter(resultsFile));
716  int col = 0;
717  //write headers
718  while (col < outline.getColumnCount()) {
719 
720  resultsWriter.write(outline.getColumnName(col));
721  col++;
722  if (col < outline.getColumnCount()) {
723  resultsWriter.write(",");
724  }
725  }
726  resultsWriter.write(System.lineSeparator());
727  //write data
728  Children resultsChildren = em.getRootContext().getChildren();
729  for (int row = 0; row < resultsChildren.getNodesCount(); row++) {
730  col = 0;
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());
734  } else {
735  resultsWriter.write(outline.getValueAt(row, col).toString());
736  }
737  col++;
738  if (col < outline.getColumnCount()) {
739  resultsWriter.write(",");
740  }
741  }
742  resultsWriter.write(System.lineSeparator());
743  }
744  resultsWriter.flush();
745  resultsWriter.close();
746  setColumnWidths();
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);
758  }
759  }
760 
765  @Messages({
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"
769  })
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) {
778  cancelSearch();
779  }
780  }
781  }
782 
783  // Variables declaration - do not modify//GEN-BEGIN:variables
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;
804  // End of variables declaration//GEN-END:variables
805 
806  /*
807  * A thread that performs a keyword search of cases
808  */
809  private final class SearchThread extends Thread {
810 
811  private final Collection<CaseNodeData> caseNodes;
812  private final SearchQuery searchQuery;
813  private final MultiCaseSearcher multiCaseSearcher = new MultiCaseSearcher();
814 
821  private SearchThread(Collection<CaseNodeData> caseNodes, SearchQuery searchQuery) {
822  this.caseNodes = caseNodes;
823  this.searchQuery = searchQuery;
824  }
825 
832  private void registerWithSearcher(Object object) {
833  multiCaseSearcher.registerWithEventBus(object);
834  }
835 
842  private void unregisterWithSearcher(Object object) {
843  multiCaseSearcher.unregisterWithEventBus(object);
844  }
845 
846  @Override
847  public void interrupt() {
848  super.interrupt();
849  //in case it is running a method which causes InterruptedExceptions to be ignored
850  multiCaseSearcher.stopMultiCaseSearch();
851  }
852 
853  @Override
854  public void run() {
855  multiCaseSearcher.performKeywordSearch(caseNodes, searchQuery, new MultiCaseKeywordSearchProgressIndicator(searchProgressBar));
856  }
857 
858  }
859 
860 }
void nodeSelectionChanged(Node[] selections, List< CaseNodeData > selectionCaseData)

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.