blob: c687d72ae6968af5cf047278fe8f3c7e85b9db41 [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.index.FieldDef;
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.index.change.ChangeField;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIndexPredicate;
import com.google.gerrit.server.query.change.DestinationPredicate;
import com.google.gerrit.server.query.change.RegexProjectPredicate;
import com.google.gerrit.server.query.change.RegexRefPredicate;
import com.google.gerrit.server.query.change.SubmitRequirementChangeQueryBuilder;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.task.statistics.HitHashMap;
import com.googlesource.gerrit.plugins.task.statistics.StopWatch;
import com.googlesource.gerrit.plugins.task.util.ThrowingProvider;
public class PredicateCache {
public static class Statistics {
protected Object predicatesByQueryCache;
}
protected final SubmitRequirementChangeQueryBuilder srcqb;
protected final TaskPluginConfiguration config;
protected final CurrentUser user;
protected final HitHashMap<String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
predicatesByQuery = new HitHashMap<>();
protected Statistics statistics;
@Inject
public PredicateCache(
TaskPluginConfiguration config, CurrentUser user, SubmitRequirementChangeQueryBuilder srcqb) {
this.config = config;
this.user = user;
this.srcqb = srcqb;
}
public void initStatistics(int summaryCount) {
statistics = new Statistics();
predicatesByQuery.initStatistics(summaryCount);
}
public Object getStatistics() {
if (statistics != null) {
statistics.predicatesByQueryCache = predicatesByQuery.getStatistics();
}
return statistics;
}
@SuppressWarnings("try")
public Predicate<ChangeData> getPredicate(String query, boolean isVisible)
throws QueryParseException {
ThrowingProvider<Predicate<ChangeData>, QueryParseException> predProvider =
predicatesByQuery.get(query);
if (predProvider != null) {
return predProvider.get();
}
// never seen 'query' before
try (StopWatch stopWatch = predicatesByQuery.createLoadingStopWatch(query, isVisible)) {
Predicate<ChangeData> pred = srcqb.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, boolean isVisible) throws QueryParseException {
if (query == null
|| "".equals(query)
|| "false".equalsIgnoreCase(query)
|| "true".equalsIgnoreCase(query)) {
return true;
}
return isCacheableByBranch(getPredicate(query, isVisible));
}
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 RegexProjectPredicate
|| predicate instanceof RegexRefPredicate) {
return true;
}
if (predicate instanceof ChangeIndexPredicate) {
FieldDef<ChangeData, ?> field = ((ChangeIndexPredicate) predicate).getField();
if (field.equals(ChangeField.PROJECT) || field.equals(ChangeField.REF)) {
return true;
}
}
return config
.getCacheableByBranchPredicateClassNames()
.contains(predicate.getClass().getName());
}
}