blob: 4da59e4da11baa81abcc1b22f76863dd38ecab82 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.googlesource.gerrit.plugins.task;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.index.query.AndPredicate;
import com.google.gerrit.index.query.NotPredicate;
import com.google.gerrit.index.query.OrPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.DestinationPredicate;
import com.google.gerrit.server.query.change.ProjectPredicate;
import com.google.gerrit.server.query.change.RefPredicate;
import com.google.gerrit.server.query.change.RegexProjectPredicate;
import com.google.gerrit.server.query.change.RegexRefPredicate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
public class PredicateCache {
protected final ChangeQueryBuilder cqb;
protected final Set<String> cacheableByBranchPredicateClassNames;
protected final CurrentUser user;
protected final Map<String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
predicatesByQuery = new HashMap<>();
@Inject
public PredicateCache(
@GerritServerConfig Config config,
@PluginName String pluginName,
CurrentUser user,
ChangeQueryBuilder cqb) {
this.user = user;
this.cqb = cqb;
cacheableByBranchPredicateClassNames =
new HashSet<>(
Arrays.asList(
config.getStringList(pluginName, "cacheable-predicates", "byBranch-className")));
}
public boolean matchWithExceptions(ChangeData c, String query)
throws QueryParseException, OrmException {
if ("true".equalsIgnoreCase(query)) {
return true;
}
return getPredicate(query).asMatchable().match(c);
}
protected Predicate<ChangeData> getPredicate(String query) throws QueryParseException {
ThrowingProvider<Predicate<ChangeData>, QueryParseException> predProvider =
predicatesByQuery.get(query);
if (predProvider != null) {
return predProvider.get();
}
// never seen 'query' before
try {
Predicate<ChangeData> pred = cqb.parse(query);
predicatesByQuery.put(query, new ThrowingProvider.Entry<>(pred));
return pred;
} catch (QueryParseException e) {
predicatesByQuery.put(query, new ThrowingProvider.Thrown<>(e));
throw e;
}
}
/**
* Can this query's output be assumed to be constant given any Change destined for the same
* Branch.NameKey?
*/
public boolean isCacheableByBranch(String query) throws QueryParseException {
if (query == null
|| "".equals(query)
|| "false".equalsIgnoreCase(query)
|| "true".equalsIgnoreCase(query)) {
return true;
}
return isCacheableByBranch(getPredicate(query));
}
protected boolean isCacheableByBranch(Predicate<ChangeData> predicate) {
if (predicate instanceof AndPredicate
|| predicate instanceof NotPredicate
|| predicate instanceof OrPredicate) {
for (Predicate<ChangeData> subPred : predicate.getChildren()) {
if (!isCacheableByBranch(subPred)) {
return false;
}
}
return true;
}
if (predicate instanceof DestinationPredicate
|| predicate instanceof ProjectPredicate
|| predicate instanceof RefPredicate
|| predicate instanceof RegexProjectPredicate
|| predicate instanceof RegexRefPredicate) {
return true;
}
return cacheableByBranchPredicateClassNames.contains(predicate.getClass().getName());
}
}