Repository: jabref/jabref
Issue: cannot open a file with invalid group search expressions

### JabRef version

5.3 (latest release)

### Operating system

GNU / Linux

### Details on version and operating system

_No response_

### Checked with the latest development build

- [X] I made a backup of my libraries before testing the latest development version.
- [X] I have tested the latest development version and the problem persists

### Steps to reproduce the behaviour



JabRef 5.4--2021-09-17--3d7521c
Linux 5.13.16-200.fc34.x86_64 amd64 
Java 16.0.2 
JavaFX 17+18

1. ... I open JabRef; it starts up with two of my key libraries opened. Good.
2. ... I try to open a specific third library. That generates the exception

Visible effect: opening the third library generates a new tab; but no table is visible. Sometimes the exception is reoccuring in a loop; other times it only shows 2 times, then I can click it away. In the latter case, when I switch to the other libraries, their table  columns are minimised, so I cannot see anything. But I can resize the column widths.

I test it with another third library. No problem.

Ok, so that means I have a library that generates that exception in interaction with JabRef. Happy to share the file confidentially.

### Appendix

...
<details>
<summary>Log File</summary>
```
java.lang.NullPointerException: Cannot invoke "java.util.List.stream()" because "this.searchResults" is null
	at org.jabref@5.4.211/org.jabref.model.search.rules.GrammarBasedSearchRule.getFulltextResults(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.rules.GrammarBasedSearchRule.applyRule(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.GroupSearchQuery.isMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.groups.SearchGroup.contains(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.groups.AbstractGroup.isMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.matchers.OrMatcher.lambda$isMatch$0(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.util.ListUtil.anyMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.matchers.OrMatcher.isMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.matchers.OrMatcher.lambda$isMatch$0(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.util.ListUtil.anyMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.search.matchers.OrMatcher.isMatch(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.groups.GroupTreeNode.getMatchingGroups(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.groups.GroupTreeNode.getMatchingGroups(Unknown Source)
	at org.jabref@5.4.211/org.jabref.model.groups.GroupTreeNode.getMatchingGroups(Unknown Source)
	at org.jabref@5.4.211/org.jabref.gui.maintable.BibEntryTableViewModel.lambda$createMatchedGroupsBinding$5(Unknown Source)
	at java.base/java.util.Optional.map(Unknown Source)
	at org.jabref@5.4.211/org.jabref.gui.maintable.BibEntryTableViewModel.lambda$createMatchedGroupsBinding$6(Unknown Source)
	at com.tobiasdiez.easybind@2.2/com.tobiasdiez.easybind.EasyBind$4.computeValue(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.beans.binding.ObjectBinding.get(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.beans.binding.ObjectExpression.getValue(Unknown Source)
	at org.jabref@5.4.211/org.jabref.gui.util.uithreadaware.UiThreadBinding.getValue(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.TableCell.updateItem(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.TableCell.indexChanged(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.IndexedCell.updateIndex(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.TableRowSkinBase.updateCells(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.TableRowSkinBase.<init>(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.TableRowSkin.<init>(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.TableRow.createDefaultSkin(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.Control.doProcessCSS(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.Control$1.doProcessCSS(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.scene.NodeHelper.processCSS(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Node.processCSS(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Node.applyCss(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.setCellIndex(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.getCell(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.getOrCreateCellSize(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.getOrCreateCellSize(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.recalculateAndImproveEstimatedSize(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.recalculateEstimatedSize(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.skin.VirtualFlow.layoutChildren(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Parent.layout(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Scene.doLayoutPass(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.Scene$ScenePulseListener.pulse(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.Toolkit.runPulse(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.Toolkit.firePulse(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.gtk.GtkApplication.enterNestedEventLoopImpl(Native Method)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.gtk.GtkApplication._enterNestedEventLoop(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.Application.enterNestedEventLoop(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.EventLoop.enter(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.stage.Stage.showAndWait(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.HeavyweightDialog.showAndWait(Unknown Source)
	at org.jabref.merged.module@5.4.211/javafx.scene.control.Dialog.showAndWait(Unknown Source)
	at org.jabref@5.4.211/org.jabref.gui.JabRefDialogService.showErrorDialogAndWait(Unknown Source)
	at org.jabref@5.4.211/org.jabref.gui.FallbackExceptionHandler.lambda$uncaughtException$0(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at org.jabref.merged.module@5.4.211/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)

```
</details>


Changed files:
src/main/java/org/jabref/model/groups/SearchGroup.java, src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java, src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java, src/test/java/org/jabref/model/groups/SearchGroupTest.java

diff --git a/src/main/java/org/jabref/model/groups/SearchGroup.java b/src/main/java/org/jabref/model/groups/SearchGroup.java
index 5e261c438b..c1df365bef 100644
--- a/src/main/java/org/jabref/model/groups/SearchGroup.java
+++ b/src/main/java/org/jabref/model/groups/SearchGroup.java
@@ -37,10 +37,10 @@ public class SearchGroup extends AbstractGroup {
             return false;
         }
         SearchGroup other = (SearchGroup) o;
-        return getName().equals(other.getName())
-                && getSearchExpression().equals(other.getSearchExpression())
-                && (getSearchFlags().equals(other.getSearchFlags()))
-                && (getHierarchicalContext() == other.getHierarchicalContext());
+        return Objects.equals(getName(), other.getName())
+               && Objects.equals(getHierarchicalContext(), other.getHierarchicalContext())
+               && Objects.equals(getSearchExpression(), other.getSearchExpression())
+               && Objects.equals(getSearchFlags(), other.getSearchFlags());
     }
 
     @Override
@@ -65,6 +65,11 @@ public class SearchGroup extends AbstractGroup {
         }
     }
 
+    @Override
+    public String toString() {
+        return "SearchGroup [query=" + query + ", name=" + name + ", searchFlags=" + getSearchFlags() + ",  context=" + context + ", color=" + color + ", isExpanded=" + isExpanded + ", description=" + description + ", iconName=" + iconName + "]";
+    }
+
     @Override
     public boolean isDynamic() {
         return true;
diff --git a/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java b/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java
index 5b0b5c40ea..777cbb6ebf 100644
--- a/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java
+++ b/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java
@@ -1,6 +1,7 @@
 package org.jabref.model.search.rules;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Objects;
@@ -51,7 +52,7 @@ public class GrammarBasedSearchRule implements SearchRule {
 
     private ParseTree tree;
     private String query;
-    private List<SearchResult> searchResults;
+    private List<SearchResult> searchResults = new ArrayList<>();
 
     private final BibDatabaseContext databaseContext;
 
@@ -99,7 +100,7 @@ public class GrammarBasedSearchRule implements SearchRule {
         tree = parser.start();
         this.query = query;
 
-        if (!searchFlags.contains(SearchRules.SearchFlags.FULLTEXT) || databaseContext == null) {
+        if (!searchFlags.contains(SearchRules.SearchFlags.FULLTEXT) || (databaseContext == null)) {
             return;
         }
         try {
diff --git a/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java b/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java
index 2e514eb6d0..29580b7be2 100644
--- a/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java
+++ b/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java
@@ -2,6 +2,7 @@ package org.jabref.logic.importer.util;
 
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.List;
 
 import javafx.scene.paint.Color;
@@ -17,8 +18,10 @@ import org.jabref.model.groups.AutomaticPersonsGroup;
 import org.jabref.model.groups.ExplicitGroup;
 import org.jabref.model.groups.GroupHierarchyType;
 import org.jabref.model.groups.GroupTreeNode;
+import org.jabref.model.groups.SearchGroup;
 import org.jabref.model.groups.TexGroup;
 import org.jabref.model.metadata.MetaData;
+import org.jabref.model.search.rules.SearchRules;
 import org.jabref.model.util.DummyFileUpdateMonitor;
 import org.jabref.model.util.FileUpdateMonitor;
 
@@ -130,4 +133,12 @@ class GroupsParserTest {
     void fromStringUnknownGroupThrowsException() throws Exception {
         assertThrows(ParseException.class, () -> GroupsParser.fromString("0 UnknownGroup:myUnknownGroup;0;;1;;;;", ',', fileMonitor, metaData));
     }
+
+    @Test
+    void fromStringParsesSearchGroup() throws Exception {
+        SearchGroup expected = new SearchGroup("Data", GroupHierarchyType.INCLUDING, "project=data|number|quant*", EnumSet.of(SearchRules.SearchFlags.REGULAR_EXPRESSION));
+        AbstractGroup parsed = GroupsParser.fromString("SearchGroup:Data;2;project=data|number|quant*;0;1;1;;;;;", ',', fileMonitor, metaData);
+        assertEquals(expected, parsed);
+
+    }
 }
diff --git a/src/test/java/org/jabref/model/groups/SearchGroupTest.java b/src/test/java/org/jabref/model/groups/SearchGroupTest.java
index 9b499ccf6e..c5b2f6a68d 100644
--- a/src/test/java/org/jabref/model/groups/SearchGroupTest.java
+++ b/src/test/java/org/jabref/model/groups/SearchGroupTest.java
@@ -7,6 +7,7 @@ import org.jabref.model.search.rules.SearchRules;
 
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class SearchGroupTest {
@@ -19,4 +20,13 @@ public class SearchGroupTest {
 
         assertTrue(group.contains(entry));
     }
+
+    @Test
+    public void containsDoesNotFindsWordWithInvalidRegularExpression() {
+        SearchGroup group = new SearchGroup("myExplicitGroup", GroupHierarchyType.INDEPENDENT, "anyfield=*rev*", EnumSet.of(SearchRules.SearchFlags.CASE_SENSITIVE, SearchRules.SearchFlags.REGULAR_EXPRESSION));
+        BibEntry entry = new BibEntry();
+        entry.addKeyword("review", ',');
+
+        assertFalse(group.contains(entry));
+    }
 }