Cache task plugin predicates

Instead of creating task predicates for every change, create them once
and cache them for the lifespan of the attributeFactory. This can result
in significant performance improvement when this lifespan correlates to
the lifespan of the query (Ibc96a754f9c21b21b131d68636274c8231df30cc).
This is particularly valuable when the predicates do expensive bulk
queries up front.

Change-Id: I1616f53dcd44ff96ecf79dfb06ba6ad056b2c343
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
index d5f539d..9167554 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.common.PluginDefinedInfo;
 import com.google.gerrit.index.query.Matchable;
+import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -35,8 +36,10 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -78,6 +81,8 @@
   protected final TaskConfigFactory taskFactory;
   protected final ChangeQueryBuilder cqb;
 
+  protected final Map<String, Predicate<ChangeData>> predicatesByQuery = new HashMap<>();
+
   protected Modules.MyOptions options;
 
   @Inject
@@ -380,7 +385,12 @@
     if (query == null || query.equalsIgnoreCase("true")) {
       return true;
     }
-    return ((Matchable) cqb.parse(query)).match(c);
+    Predicate<ChangeData> pred = predicatesByQuery.get(query);
+    if (pred == null) {
+      pred = cqb.parse(query);
+      predicatesByQuery.put(query, pred);
+    }
+    return ((Matchable) pred).match(c);
   }
 
   protected Boolean matchOrNull(ChangeData c, String query) {