Merge "Allow plugins to define 'has' search operands."

This commit is contained in:
Martin Fick 2016-11-16 14:58:46 +00:00 committed by Gerrit Code Review
commit 56c3634d05
3 changed files with 82 additions and 10 deletions

View File

@ -684,6 +684,43 @@ public class SampleOperator
} }
---- ----
[[search_operands]]
=== Search Operands ===
Plugins can define new search operands to extend change searching.
Plugin methods implementing search operands (returning a
`Predicate<ChangeData>`), must be defined on a class implementing
one of the `ChangeQueryBuilder.ChangeOperandsFactory` interfaces
(.e.g., ChangeQueryBuilder.ChangeHasOperandFactory). The specific
`ChangeOperandFactory` class must also be bound to the `DynamicSet` from
a module's `configure()` method in the plugin.
The new operand, when used in a search would appear as:
operatorName:operandName_pluginName
A sample `ChangeHasOperandFactory` class implementing, and registering, a
new `has:sample_pluginName` operand is shown below:
====
@Singleton
public class SampleHasOperand implements ChangeHasOperandFactory {
public static class Module extends AbstractModule {
@Override
protected void configure() {
bind(ChangeHasOperandFactory.class)
.annotatedWith(Exports.named("sample")
.to(SampleHasOperand.class);
}
}
@Override
public Predicate<ChangeData> create(ChangeQueryBuilder builder)
throws QueryParseException {
return new HasSamplePredicate();
}
====
[[simple-configuration]] [[simple-configuration]]
== Simple Configuration in `gerrit.config` == Simple Configuration in `gerrit.config`

View File

@ -376,6 +376,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), UploadValidationListener.class); DynamicSet.setOf(binder(), UploadValidationListener.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class); DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
install(new GitwebConfig.LegacyModule(cfg)); install(new GitwebConfig.LegacyModule(cfg));
bind(AnonymousUser.class); bind(AnonymousUser.class);

View File

@ -28,6 +28,7 @@ import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.errors.NotSignedInException; import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
@ -98,6 +99,27 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
extends OperatorFactory<ChangeData, ChangeQueryBuilder> { extends OperatorFactory<ChangeData, ChangeQueryBuilder> {
} }
/**
* Converts a operand (operator value) passed to an operator into a
* {@link Predicate}.
*
* Register a ChangeOperandFactory in a config Module like this (note, for
* an example we are using the has predicate, when other predicate plugin
* operands are created they can be registered in a similar manner):
*
* bind(ChangeHasOperandFactory.class)
* .annotatedWith(Exports.named("your has operand"))
* .to(YourClass.class);
*
*/
private interface ChangeOperandFactory {
Predicate<ChangeData> create(ChangeQueryBuilder builder)
throws QueryParseException;
}
public interface ChangeHasOperandFactory extends ChangeOperandFactory {
}
private static final Pattern PAT_LEGACY_ID = Pattern.compile("^[1-9][0-9]*$"); private static final Pattern PAT_LEGACY_ID = Pattern.compile("^[1-9][0-9]*$");
private static final Pattern PAT_CHANGE_ID = Pattern.compile(CHANGE_ID_PATTERN); private static final Pattern PAT_CHANGE_ID = Pattern.compile(CHANGE_ID_PATTERN);
private static final Pattern DEF_CHANGE = Pattern.compile( private static final Pattern DEF_CHANGE = Pattern.compile(
@ -166,6 +188,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final Provider<InternalChangeQuery> queryProvider; final Provider<InternalChangeQuery> queryProvider;
final ChangeIndexRewriter rewriter; final ChangeIndexRewriter rewriter;
final DynamicMap<ChangeOperatorFactory> opFactories; final DynamicMap<ChangeOperatorFactory> opFactories;
final DynamicMap<ChangeHasOperandFactory> hasOperands;
final IdentifiedUser.GenericFactory userFactory; final IdentifiedUser.GenericFactory userFactory;
final CapabilityControl.Factory capabilityControlFactory; final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory; final ChangeControl.GenericFactory changeControlGenericFactory;
@ -199,6 +222,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
Provider<InternalChangeQuery> queryProvider, Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter, ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories, DynamicMap<ChangeOperatorFactory> opFactories,
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory, IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self, Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory, CapabilityControl.Factory capabilityControlFactory,
@ -224,11 +248,9 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
StarredChangesUtil starredChangesUtil, StarredChangesUtil starredChangesUtil,
AccountCache accountCache, AccountCache accountCache,
@GerritServerConfig Config cfg) { @GerritServerConfig Config cfg) {
this(db, queryProvider, rewriter, opFactories, userFactory, self, this(db, queryProvider, rewriter, opFactories, hasOperands, userFactory,
capabilityControlFactory, changeControlGenericFactory, notesFactory, self, capabilityControlFactory, changeControlGenericFactory, notesFactory, changeDataFactory, fillArgs, commentsUtil,
changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend, accountResolver, groupBackend, allProjectsName, allUsersName, patchListCache, repoManager, projectCache, listChildProjects, submitDryRun, conflictsCache,
allProjectsName, allUsersName, patchListCache, repoManager,
projectCache, listChildProjects, submitDryRun, conflictsCache,
trackingFooters, indexes != null ? indexes.getSearchIndex() : null, trackingFooters, indexes != null ? indexes.getSearchIndex() : null,
indexConfig, listMembers, starredChangesUtil, accountCache, indexConfig, listMembers, starredChangesUtil, accountCache,
cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true)); cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true));
@ -239,6 +261,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
Provider<InternalChangeQuery> queryProvider, Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter, ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories, DynamicMap<ChangeOperatorFactory> opFactories,
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory, IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self, Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory, CapabilityControl.Factory capabilityControlFactory,
@ -293,15 +316,16 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
this.starredChangesUtil = starredChangesUtil; this.starredChangesUtil = starredChangesUtil;
this.accountCache = accountCache; this.accountCache = accountCache;
this.allowsDrafts = allowsDrafts; this.allowsDrafts = allowsDrafts;
this.hasOperands = hasOperands;
} }
Arguments asUser(CurrentUser otherUser) { Arguments asUser(CurrentUser otherUser) {
return new Arguments(db, queryProvider, rewriter, opFactories, userFactory, return new Arguments(db, queryProvider, rewriter, opFactories,
Providers.of(otherUser), hasOperands, userFactory, Providers.of(otherUser),
capabilityControlFactory, changeControlGenericFactory, notesFactory, capabilityControlFactory, changeControlGenericFactory, notesFactory,
changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend, changeDataFactory, fillArgs, commentsUtil, accountResolver,
allProjectsName, allUsersName, patchListCache, repoManager, groupBackend, allProjectsName, allUsersName, patchListCache,
projectCache, listChildProjects, submitDryRun, repoManager, projectCache, listChildProjects, submitDryRun,
conflictsCache, trackingFooters, index, indexConfig, listMembers, conflictsCache, trackingFooters, index, indexConfig, listMembers,
starredChangesUtil, accountCache, allowsDrafts); starredChangesUtil, accountCache, allowsDrafts);
} }
@ -453,6 +477,16 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
if ("edit".equalsIgnoreCase(value)) { if ("edit".equalsIgnoreCase(value)) {
return new EditByPredicate(self()); return new EditByPredicate(self());
} }
// for plugins the value will be operandName_pluginName
String[] names = value.split("_");
if (names.length == 2) {
ChangeHasOperandFactory op = args.hasOperands.get(names[1], names[0]);
if (op != null) {
return op.create(this);
}
}
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }