package org.jabref.gui.search; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import javax.swing.SwingWorker; import org.jabref.JabRefGUI; import org.jabref.gui.BasePanel; import org.jabref.gui.BasePanelMode; import org.jabref.gui.maintable.MainTableDataModel; import org.jabref.logic.search.SearchQuery; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Not reusable. Always create a new instance for each search! */ class SearchWorker extends SwingWorker<List<BibEntry>, Void> { private static final Log LOGGER = LogFactory.getLog(SearchWorker.class); private final BasePanel basePanel; private final BibDatabase database; private final SearchQuery searchQuery; private final SearchDisplayMode searchDisplayMode; public SearchWorker(BasePanel basePanel, SearchQuery searchQuery, SearchDisplayMode searchDisplayMode) { this.basePanel = Objects.requireNonNull(basePanel); this.database = Objects.requireNonNull(basePanel.getDatabase()); this.searchQuery = Objects.requireNonNull(searchQuery); this.searchDisplayMode = Objects.requireNonNull(searchDisplayMode); LOGGER.debug("Search (" + this.searchDisplayMode.getDisplayName() + "): " + this.searchQuery); } @Override protected List<BibEntry> doInBackground() throws Exception { return database.getEntries().parallelStream() .filter(searchQuery::isMatch) .collect(Collectors.toList()); } @Override protected void done() { if (isCancelled()) { return; } try { updateUIWithSearchResult(get()); } catch (InterruptedException | ExecutionException e) { LOGGER.error("something went wrong during the search", e); } } private void updateUIWithSearchResult(List<BibEntry> matchedEntries) { GlobalSearchBar globalSearchBar = JabRefGUI.getMainFrame().getGlobalSearchBar(); // check if still the current query if (!globalSearchBar.isStillValidQuery(searchQuery)) { // do not update - another search was already issued return; } // clear for (BibEntry entry : basePanel.getDatabase().getEntries()) { entry.setSearchHit(false); } // and mark for (BibEntry entry : matchedEntries) { entry.setSearchHit(true); } basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); // Show the result in the chosen way: switch (searchDisplayMode) { case FLOAT: basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FLOAT); break; case FILTER: basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FILTER); break; default: LOGGER.error("Following searchDisplayMode was not defined: " + searchDisplayMode); break; } // only selects the first match if the selected entries are no hits or no entry is selected // and no editor is open (to avoid jumping around when editing an entry) if (basePanel.getMode() != BasePanelMode.SHOWING_EDITOR && basePanel.getMode() != BasePanelMode.WILL_SHOW_EDITOR) { List<BibEntry> selectedEntries = basePanel.getSelectedEntries(); boolean isHitSelected = selectedEntries.stream().anyMatch(BibEntry::isSearchHit); if (!isHitSelected && !matchedEntries.isEmpty()) { for (int i = 0; i < basePanel.getMainTable().getRowCount(); i++) { BibEntry entry = basePanel.getMainTable().getEntryAt(i); if (entry.isSearchHit()) { basePanel.getMainTable().setSelected(i); break; } } } } globalSearchBar.updateResults(matchedEntries.size(), searchQuery.getDescription(), searchQuery.isGrammarBasedSearch()); globalSearchBar.getSearchQueryHighlightObservable().fireSearchlistenerEvent(searchQuery); } }