19 package org.sleuthkit.autopsy.filesearch;
 
   21 import java.awt.Component;
 
   22 import java.awt.Cursor;
 
   23 import java.awt.GridLayout;
 
   24 import java.awt.event.ActionEvent;
 
   25 import java.awt.event.ActionListener;
 
   26 import java.beans.PropertyChangeEvent;
 
   27 import java.beans.PropertyChangeListener;
 
   28 import java.util.ArrayList;
 
   29 import java.util.Collection;
 
   30 import java.util.Collections;
 
   31 import java.util.List;
 
   32 import java.util.logging.Level;
 
   33 import javax.swing.JLabel;
 
   34 import javax.swing.JPanel;
 
   35 import javax.swing.border.EmptyBorder;
 
   36 import org.openide.DialogDisplayer;
 
   37 import org.openide.NotifyDescriptor;
 
   38 import org.openide.nodes.Node;
 
   39 import org.openide.util.NbBundle;
 
   40 import org.openide.windows.TopComponent;
 
   56 @SuppressWarnings(
"PMD.SingularField") 
 
   57 class FileSearchPanel extends javax.swing.JPanel {
 
   59     private final List<FileSearchFilter> filters = 
new ArrayList<>();
 
   60     private static int resultWindowCount = 0; 
 
   61     private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, 
"FileSearchPanel.emptyWhereClause.text");
 
   72         customizeComponents();
 
   79     private void customizeComponents() {
 
   81         JLabel label = 
new JLabel(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.custComp.label.text"));
 
   82         label.setAlignmentX(Component.LEFT_ALIGNMENT);
 
   83         label.setBorder(
new EmptyBorder(0, 0, 10, 0));
 
   85         JPanel panel1 = 
new JPanel();
 
   86         panel1.setLayout(
new GridLayout(1,2));
 
   87         panel1.add(
new JLabel(
""));
 
   88         JPanel panel2 = 
new JPanel();
 
   89         panel2.setLayout(
new GridLayout(1,2, 20, 0));
 
   90         JPanel panel3 = 
new JPanel();
 
   91         panel3.setLayout(
new GridLayout(1,2, 20, 0));
 
   92         JPanel panel4 = 
new JPanel();
 
   93         panel4.setLayout(
new GridLayout(1,2, 20, 0));
 
   94         JPanel panel5 = 
new JPanel();
 
   95         panel5.setLayout(
new GridLayout(1,2, 20, 0));
 
   98         NameSearchFilter nameFilter =  
new NameSearchFilter();
 
   99         SizeSearchFilter sizeFilter = 
new SizeSearchFilter();
 
  100         DateSearchFilter dateFilter = 
new DateSearchFilter();
 
  101         KnownStatusSearchFilter knowStatusFilter = 
new KnownStatusSearchFilter();
 
  102         HashSearchFilter hashFilter = 
new HashSearchFilter();
 
  103         MimeTypeFilter mimeTypeFilter = 
new MimeTypeFilter();
 
  104         DataSourceFilter dataSourceFilter = 
new DataSourceFilter();
 
  106         panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.filterTitle.name"),nameFilter));
 
  108         panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.filterTitle.metadata"),sizeFilter));
 
  110         panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.filterTitle.metadata"), dateFilter)); 
 
  111         panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.filterTitle.knownStatus"), knowStatusFilter));
 
  113         panel5.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"HashSearchPanel.md5CheckBox.text"), hashFilter));
 
  114         panel5.add(
new JLabel(
""));
 
  115         panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.filterTitle.metadata"), mimeTypeFilter));
 
  116         panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(), 
"DataSourcePanel.dataSourceCheckBox.text"), dataSourceFilter));
 
  117         filterPanel.add(panel1);
 
  118         filterPanel.add(panel2);
 
  119         filterPanel.add(panel3);
 
  120         filterPanel.add(panel4);
 
  121         filterPanel.add(panel5);
 
  123         filters.add(nameFilter);
 
  124         filters.add(sizeFilter);
 
  125         filters.add(dateFilter);
 
  126         filters.add(knowStatusFilter);
 
  127         filters.add(hashFilter);
 
  128         filters.add(mimeTypeFilter);
 
  129         filters.add(dataSourceFilter);
 
  131         for (FileSearchFilter filter : this.getFilters()) {
 
  132             filter.addPropertyChangeListener(
new PropertyChangeListener() {
 
  134                 public void propertyChange(PropertyChangeEvent evt) {
 
  135                     searchButton.setEnabled(isValidSearch());
 
  139         addListenerToAll(
new ActionListener() {
 
  141             public void actionPerformed(ActionEvent e) {
 
  145         searchButton.setEnabled(isValidSearch());
 
  151     private boolean isValidSearch() {
 
  152         boolean enabled = 
false;
 
  153         for (FileSearchFilter filter : this.getFilters()) {
 
  154             if (filter.isEnabled()) {
 
  156                 if (!filter.isValid()) {
 
  157                     errorLabel.setText(filter.getLastError());
 
  163         errorLabel.setText(
"");
 
  171     @NbBundle.Messages(
"FileSearchPanel.emptyNode.display.text=No results found.")
 
  172     private void search() {
 
  174         this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  176             if (this.isValidSearch()) {
 
  177                 String title = NbBundle.getMessage(this.getClass(), 
"FileSearchPanel.search.results.title", ++resultWindowCount);
 
  178                 String pathText = NbBundle.getMessage(this.getClass(), 
"FileSearchPanel.search.results.pathText");
 
  181                 Case currentCase = Case.getCurrentCaseThrows(); 
 
  182                 long totalMatches = 0;
 
  183                 List<AbstractFile> contentList = null;
 
  185                     SleuthkitCase tskDb = currentCase.getSleuthkitCase();
 
  186                     contentList = tskDb.findAllFilesWhere(this.getQuery());
 
  188                 } 
catch (TskCoreException ex) {
 
  189                     Logger logger = Logger.getLogger(this.getClass().getName());
 
  190                     logger.log(Level.WARNING, 
"Error while trying to get the number of matches.", ex); 
 
  193                 if (contentList == null) {
 
  194                     contentList = Collections.<AbstractFile>emptyList();
 
  197                 SearchNode sn = 
new SearchNode(contentList);
 
  198                 TableFilterNode tableFilterNode = 
new TableFilterNode(sn, 
true, sn.getName());
 
  199                 final TopComponent searchResultWin;
 
  200                 if (contentList.isEmpty()) {
 
  201                     Node emptyNode = 
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()), 
true);
 
  202                     searchResultWin = DataResultTopComponent.createInstance(title, pathText,
 
  205                     searchResultWin = DataResultTopComponent.createInstance(title, pathText,
 
  206                         tableFilterNode, contentList.size());
 
  208                 searchResultWin.requestActive(); 
 
  215                 if (totalMatches > 10000) {
 
  217                     String msg = NbBundle.getMessage(this.getClass(), 
"FileSearchPanel.search.results.msg", totalMatches);
 
  218                     String details = NbBundle.getMessage(this.getClass(), 
"FileSearchPanel.search.results.details");
 
  219                     MessageNotifyUtil.Notify.info(msg, details);
 
  222                 throw new FilterValidationException(
 
  223                         NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.search.exception.noFilterSelected.msg"));
 
  225         } 
catch (FilterValidationException | NoCurrentCaseException ex) {
 
  226             NotifyDescriptor d = 
new NotifyDescriptor.Message(
 
  227                     NbBundle.getMessage(
this.getClass(), 
"FileSearchPanel.search.validationErr.msg", ex.getMessage()));
 
  228             DialogDisplayer.getDefault().notify(d);
 
  230             this.setCursor(null);
 
  252     private String getQuery() throws FilterValidationException {
 
  257         for (FileSearchFilter f : this.getEnabledFilters()) {
 
  258             String result = f.getPredicate();
 
  259             if (!result.isEmpty()) {
 
  261                     query += 
" AND (" + result + 
")"; 
 
  263                     query += 
" (" + result + 
")"; 
 
  269         if (query.isEmpty()) {
 
  270             throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
 
  275     private Collection<FileSearchFilter> getFilters() {
 
  279     private Collection<FileSearchFilter> getEnabledFilters() {
 
  280         Collection<FileSearchFilter> enabledFilters = 
new ArrayList<>();
 
  282         for (FileSearchFilter f : this.getFilters()) {
 
  284                 enabledFilters.add(f);
 
  288         return enabledFilters;
 
  291     void addListenerToAll(ActionListener l) {
 
  292         searchButton.addActionListener(l);
 
  293         for (FileSearchFilter fsf : getFilters()) {
 
  294             fsf.addActionListener(l);
 
  303     @SuppressWarnings(
"unchecked")
 
  305     private 
void initComponents() {
 
  307         filterPanel = 
new javax.swing.JPanel();
 
  308         searchButton = 
new javax.swing.JButton();
 
  309         errorLabel = 
new javax.swing.JLabel();
 
  311         setPreferredSize(
new java.awt.Dimension(300, 300));
 
  313         filterPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
 
  314         filterPanel.setPreferredSize(
new java.awt.Dimension(300, 400));
 
  315         filterPanel.setLayout(
new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS));
 
  317         searchButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class, 
"FileSearchPanel.searchButton.text")); 
 
  319         errorLabel.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class, 
"FileSearchPanel.errorLabel.text")); 
 
  320         errorLabel.setForeground(
new java.awt.Color(255, 51, 51));
 
  322         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
  323         this.setLayout(layout);
 
  324         layout.setHorizontalGroup(
 
  325             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  326             .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  327             .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
 
  329                 .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  330                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  331                 .addComponent(searchButton)
 
  334         layout.setVerticalGroup(
 
  335             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  336             .addGroup(layout.createSequentialGroup()
 
  337                 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
 
  338                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
 
  339                     .addComponent(searchButton)
 
  340                     .addComponent(errorLabel))
 
  346     private javax.swing.JLabel errorLabel;
 
  347     private javax.swing.JPanel filterPanel;
 
  348     private javax.swing.JButton searchButton;