From ba0d0adf3b623ae263e496bfde65f9b7d56da4a4 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Wed, 2 Oct 2019 22:35:14 +0530 Subject: [PATCH] Handle regular expressions in global search [WIP] Fixes #5349 Validate regex before setting activesearch. Adds a validator to the search field Fixes #5349 --- .../org/jabref/gui/entryeditor/SourceTab.java | 7 +++- .../org/jabref/gui/preview/PreviewViewer.java | 7 +++- .../jabref/gui/search/GlobalSearchBar.java | 35 ++++++++++++++++++- src/main/resources/l10n/JabRef_en.properties | 1 + 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index 50683c26d81..bf7808c4632 100644 --- a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java @@ -8,6 +8,7 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javax.swing.undo.UndoManager; @@ -112,7 +113,11 @@ public SourceTab(BibDatabaseContext bibDatabaseContext, CountingUndoManager undo this.stateManager = stateManager; stateManager.activeSearchQueryProperty().addListener((observable, oldValue, newValue) -> { - searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); + try { + searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); + } catch (PatternSyntaxException e) { + LOGGER.error(e.getMessage()); + } highlightSearchPattern(); }); diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index c3d0ec4b405..b2e7b2d2730 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -3,6 +3,7 @@ import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -62,7 +63,11 @@ public class PreviewViewer extends ScrollPane implements InvalidationListener { private boolean registered; private ChangeListener> listener = (queryObservable, queryOldValue, queryNewValue) -> { - searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); + try { + searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); + } catch (PatternSyntaxException e) { + LOGGER.error(e.getMessage()); + } highlightSearchPattern(); }; diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 6938411b4ad..41d4cbdf4dc 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -4,10 +4,13 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.css.PseudoClass; import javafx.event.Event; @@ -48,6 +51,7 @@ import org.jabref.gui.search.rules.describer.SearchDescribers; import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.DefaultTaskExecutor; +import org.jabref.gui.util.IconValidationDecorator; import org.jabref.gui.util.TooltipTextUtil; import org.jabref.logic.l10n.Localization; import org.jabref.logic.search.SearchQuery; @@ -55,6 +59,10 @@ import org.jabref.preferences.JabRefPreferences; import org.jabref.preferences.SearchPreferences; +import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; +import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.Validator; +import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; import impl.org.controlsfx.skin.AutoCompletePopup; import org.controlsfx.control.textfield.AutoCompletionBinding; import org.fxmisc.easybind.EasyBind; @@ -81,6 +89,7 @@ public class GlobalSearchBar extends HBox { private final Tooltip tooltip = new Tooltip(); private final StateManager stateManager; private SearchDisplayMode searchDisplayMode; + private Validator regexValidator; public GlobalSearchBar(JabRefFrame frame, StateManager stateManager) { super(); @@ -137,6 +146,15 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager) { searchField.setMaxWidth(initialSize); HBox.setHgrow(searchField, Priority.ALWAYS); + regexValidator = new FunctionBasedValidator<>( + searchField.textProperty(), + query -> !(regularExp.isSelected() && !validRegex()), + ValidationMessage.error(Localization.lang("Invalid regular expression")) + ); + ControlsFxVisualizer visualizer = new ControlsFxVisualizer(); + visualizer.setDecoration(new IconValidationDecorator(Pos.CENTER_LEFT)); + Platform.runLater(() -> { visualizer.initVisualization(regexValidator.getValidationStatus(), searchField); }); + Timer searchTask = FxTimer.create(java.time.Duration.ofMillis(SEARCH_DELAY), this::performSearch); searchField.textProperty().addListener((observable, oldValue, newValue) -> searchTask.restart()); @@ -219,15 +237,30 @@ public void performSearch() { return; } + // Invalid regular expression + if (!regexValidator.getValidationStatus().isValid()) { + currentResults.setText(Localization.lang("Invalid regular expression")); + return; + } + SearchQuery searchQuery = new SearchQuery(this.searchField.getText(), this.caseSensitive.isSelected(), this.regularExp.isSelected()); if (!searchQuery.isValid()) { informUserAboutInvalidSearchQuery(); return; } - stateManager.setSearchQuery(searchQuery); } + private boolean validRegex() { + try { + Pattern.compile(searchField.getText()); + } catch (PatternSyntaxException e) { + LOGGER.debug(e.getMessage()); + return false; + } + return true; + } + private void informUserAboutInvalidSearchQuery() { searchField.pseudoClassStateChanged(CLASS_NO_RESULTS, true); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 283a0fd5ddc..b8fc9524ac0 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1399,6 +1399,7 @@ should\ contain\ a\ four\ digit\ number=should contain a four digit number should\ contain\ a\ valid\ page\ number\ range=should contain a valid page number range No\ results\ found.=No results found. Found\ %0\ results.=Found %0 results. +Invalid\ Regex=Invalid Regex plain\ text=plain text This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ regular\ expression\ %0=This search contains entries in which any field contains the regular expression %0 This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ term\ %0=This search contains entries in which any field contains the term %0