From 1664ca252ea9aa02cb041d336dd7ce7118b344d1 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Wed, 2 Oct 2019 22:35:14 +0530 Subject: [PATCH 1/3] Handle regular expressions in global search [WIP] Fixes #5349 --- src/main/java/org/jabref/gui/entryeditor/SourceTab.java | 7 ++++++- src/main/java/org/jabref/gui/preview/PreviewViewer.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index 50683c26d815..bf7808c4632f 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 8811d398b888..977f45920f8e 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; @@ -64,7 +65,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(); }; From fa5694899af035383073acc810e23b3e326ece25 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Sat, 5 Oct 2019 17:40:14 +0530 Subject: [PATCH 2/3] Validate regex before setting activesearch. --- .../org/jabref/gui/entryeditor/SourceTab.java | 7 +------ .../org/jabref/gui/preview/PreviewViewer.java | 7 +------ .../org/jabref/gui/search/GlobalSearchBar.java | 17 +++++++++++++++++ src/main/resources/l10n/JabRef_en.properties | 1 + 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index bf7808c4632f..50683c26d815 100644 --- a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java @@ -8,7 +8,6 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import javax.swing.undo.UndoManager; @@ -113,11 +112,7 @@ public SourceTab(BibDatabaseContext bibDatabaseContext, CountingUndoManager undo this.stateManager = stateManager; stateManager.activeSearchQueryProperty().addListener((observable, oldValue, newValue) -> { - try { - searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); - } catch (PatternSyntaxException e) { - LOGGER.error(e.getMessage()); - } + searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); highlightSearchPattern(); }); diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index 977f45920f8e..8811d398b888 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -3,7 +3,6 @@ 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; @@ -65,11 +64,7 @@ public class PreviewViewer extends ScrollPane implements InvalidationListener { private boolean registered; private ChangeListener> listener = (queryObservable, queryOldValue, queryNewValue) -> { - try { - searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); - } catch (PatternSyntaxException e) { - LOGGER.error(e.getMessage()); - } + searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); highlightSearchPattern(); }; diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 6938411b4ad6..5e30c26701bb 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -4,6 +4,8 @@ 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; @@ -219,6 +221,11 @@ public void performSearch() { return; } + if (regularExp.isSelected() && !validRegex()) { + currentResults.setText(Localization.lang("Invalid Regex")); + return ; + } + SearchQuery searchQuery = new SearchQuery(this.searchField.getText(), this.caseSensitive.isSelected(), this.regularExp.isSelected()); if (!searchQuery.isValid()) { informUserAboutInvalidSearchQuery(); @@ -228,6 +235,16 @@ public void performSearch() { stateManager.setSearchQuery(searchQuery); } + private boolean validRegex() { + try { + Pattern.compile(searchField.getText()); + } catch (PatternSyntaxException e) { + LOGGER.error(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 283a0fd5ddc6..b8fc9524ac09 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 From 5a18fb7e3f7a824b0bb3be20db2ce917151bf9b1 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Tue, 15 Oct 2019 19:27:31 +0530 Subject: [PATCH 3/3] Adds a validator to the search field --- .../jabref/gui/search/GlobalSearchBar.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 5e30c26701bb..65c947f15208 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -4,12 +4,14 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; 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; @@ -50,6 +52,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; @@ -57,6 +60,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; @@ -83,6 +90,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(); @@ -139,6 +147,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()); @@ -221,9 +238,10 @@ public void performSearch() { return; } - if (regularExp.isSelected() && !validRegex()) { - currentResults.setText(Localization.lang("Invalid Regex")); - 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()); @@ -231,7 +249,6 @@ public void performSearch() { informUserAboutInvalidSearchQuery(); return; } - stateManager.setSearchQuery(searchQuery); } @@ -239,7 +256,7 @@ private boolean validRegex() { try { Pattern.compile(searchField.getText()); } catch (PatternSyntaxException e) { - LOGGER.error(e.getMessage()); + LOGGER.debug(e.getMessage()); return false; } return true;