diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index 9c98ca1031..41c464f8be 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -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`), 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 create(ChangeQueryBuilder builder) + throws QueryParseException { + return new HasSamplePredicate(); + } +==== + + [[simple-configuration]] == Simple Configuration in `gerrit.config` diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java index 60db49e9a6..6dd0fabbba 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java @@ -376,6 +376,7 @@ public class GerritGlobalModule extends FactoryModule { DynamicSet.setOf(binder(), UploadValidationListener.class); DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class); + DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class); install(new GitwebConfig.LegacyModule(cfg)); bind(AnonymousUser.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index e62bf48b13..3db9e95358 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java @@ -28,6 +28,7 @@ import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.errors.NotSignedInException; 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.AccountGroup; import com.google.gerrit.reviewdb.client.Branch; @@ -98,6 +99,27 @@ public class ChangeQueryBuilder extends QueryBuilder { extends OperatorFactory { } + /** + * 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 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_CHANGE_ID = Pattern.compile(CHANGE_ID_PATTERN); private static final Pattern DEF_CHANGE = Pattern.compile( @@ -166,6 +188,7 @@ public class ChangeQueryBuilder extends QueryBuilder { final Provider queryProvider; final ChangeIndexRewriter rewriter; final DynamicMap opFactories; + final DynamicMap hasOperands; final IdentifiedUser.GenericFactory userFactory; final CapabilityControl.Factory capabilityControlFactory; final ChangeControl.GenericFactory changeControlGenericFactory; @@ -199,6 +222,7 @@ public class ChangeQueryBuilder extends QueryBuilder { Provider queryProvider, ChangeIndexRewriter rewriter, DynamicMap opFactories, + DynamicMap hasOperands, IdentifiedUser.GenericFactory userFactory, Provider self, CapabilityControl.Factory capabilityControlFactory, @@ -224,11 +248,9 @@ public class ChangeQueryBuilder extends QueryBuilder { StarredChangesUtil starredChangesUtil, AccountCache accountCache, @GerritServerConfig Config cfg) { - this(db, queryProvider, rewriter, opFactories, userFactory, self, - capabilityControlFactory, changeControlGenericFactory, notesFactory, - changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend, - allProjectsName, allUsersName, patchListCache, repoManager, - projectCache, listChildProjects, submitDryRun, conflictsCache, + this(db, queryProvider, rewriter, opFactories, hasOperands, userFactory, + self, capabilityControlFactory, changeControlGenericFactory, notesFactory, changeDataFactory, fillArgs, commentsUtil, + accountResolver, groupBackend, allProjectsName, allUsersName, patchListCache, repoManager, projectCache, listChildProjects, submitDryRun, conflictsCache, trackingFooters, indexes != null ? indexes.getSearchIndex() : null, indexConfig, listMembers, starredChangesUtil, accountCache, cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true)); @@ -239,6 +261,7 @@ public class ChangeQueryBuilder extends QueryBuilder { Provider queryProvider, ChangeIndexRewriter rewriter, DynamicMap opFactories, + DynamicMap hasOperands, IdentifiedUser.GenericFactory userFactory, Provider self, CapabilityControl.Factory capabilityControlFactory, @@ -293,15 +316,16 @@ public class ChangeQueryBuilder extends QueryBuilder { this.starredChangesUtil = starredChangesUtil; this.accountCache = accountCache; this.allowsDrafts = allowsDrafts; + this.hasOperands = hasOperands; } Arguments asUser(CurrentUser otherUser) { - return new Arguments(db, queryProvider, rewriter, opFactories, userFactory, - Providers.of(otherUser), + return new Arguments(db, queryProvider, rewriter, opFactories, + hasOperands, userFactory, Providers.of(otherUser), capabilityControlFactory, changeControlGenericFactory, notesFactory, - changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend, - allProjectsName, allUsersName, patchListCache, repoManager, - projectCache, listChildProjects, submitDryRun, + changeDataFactory, fillArgs, commentsUtil, accountResolver, + groupBackend, allProjectsName, allUsersName, patchListCache, + repoManager, projectCache, listChildProjects, submitDryRun, conflictsCache, trackingFooters, index, indexConfig, listMembers, starredChangesUtil, accountCache, allowsDrafts); } @@ -453,6 +477,16 @@ public class ChangeQueryBuilder extends QueryBuilder { if ("edit".equalsIgnoreCase(value)) { 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(); }