]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6857 WS components/search search for components
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 30 Sep 2015 16:39:59 +0000 (18:39 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 2 Oct 2015 07:23:02 +0000 (09:23 +0200)
15 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/ResourceTypeFunctions.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/WsComponentsParameters.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/permission/ws/ResourceTypeToQualifier.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/permission/ws/SearchProjectPermissionsDataLoader.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/DefaultPermissionTemplateFinder.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/SetDefaultTemplateAction.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/resources/org/sonar/server/component/ws/search-components-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java [new file with mode: 0644]
sonar-ws/src/main/gen-java/org/sonarqube/ws/WsComponents.java [new file with mode: 0644]
sonar-ws/src/main/protobuf/ws-components.proto [new file with mode: 0644]

diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ResourceTypeFunctions.java b/server/sonar-server/src/main/java/org/sonar/server/component/ResourceTypeFunctions.java
new file mode 100644 (file)
index 0000000..7c95cbe
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.component;
+
+import com.google.common.base.Function;
+import javax.annotation.Nonnull;
+import org.sonar.api.resources.ResourceType;
+
+public class ResourceTypeFunctions {
+
+  public static final Function<ResourceType, String> RESOURCE_TYPE_TO_QUALIFIER = ResourceTypeToQualifier.INSTANCE;
+
+  private enum ResourceTypeToQualifier implements Function<ResourceType, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(@Nonnull ResourceType resourceType) {
+      return resourceType.getQualifier();
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java
new file mode 100644 (file)
index 0000000..6e80a56
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.component.ws;
+
+import org.sonar.core.platform.Module;
+
+public class ComponentsWsModule extends Module {
+  @Override
+  protected void configureModule() {
+    add(
+      ResourcesWs.class,
+      ComponentsWs.class,
+      EventsWs.class,
+      // actions
+      AppAction.class,
+      SearchAction.class,
+      SearchViewComponentsAction.class);
+  }
+}
index 36bb872266906c13fa60a779bdf2fb8b463eef33..5078bd381b61e05c166c9ab1aa8de2bb6c32f615 100644 (file)
 
 package org.sonar.server.component.ws;
 
+import com.google.common.base.Function;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.Paging;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentQuery;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsComponents.WsSearchResponse;
+
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Ordering.natural;
+import static java.lang.String.format;
+import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER;
+import static org.sonar.server.component.ws.WsComponentsParameters.PARAM_QUALIFIERS;
+import static org.sonar.server.permission.ws.WsPermissionParameters.PARAM_QUALIFIER;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class SearchAction implements ComponentsWsAction {
+  private static final String QUALIFIER_PROPERTY_PREFIX = "qualifiers.";
+
+  private final DbClient dbClient;
+  private final ResourceTypes resourceTypes;
+  private final I18n i18n;
+  private final UserSession userSession;
+
+  public SearchAction(DbClient dbClient, ResourceTypes resourceTypes, I18n i18n, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.resourceTypes = resourceTypes;
+    this.i18n = i18n;
+    this.userSession = userSession;
+  }
+
   @Override
   public void define(WebService.NewController context) {
     WebService.NewAction action = context.createAction("search")
       .setSince("5.2")
+      .setInternal(true)
       .setDescription("Search for components")
       .addPagingParams(100)
-      .addSearchQuery("sona", "project names")
-      .setResponseExample(getClass().getResource("search-example.json"))
+      .addSearchQuery("sona", "component names", "component keys")
+      .setResponseExample(getClass().getResource("search-components-example.json"))
       .setHandler(this);
+
+    action.createParam(PARAM_QUALIFIERS)
+      .setRequired(true)
+      .setExampleValue(format("%s,%s", Qualifiers.PROJECT, Qualifiers.MODULE))
+      .setDescription("Comma-separated list of component qualifiers. Possible values are " + buildQualifiersDescription());
   }
 
   @Override
   public void handle(Request wsRequest, Response wsResponse) throws Exception {
+    userSession.checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+
+    List<String> qualifiers = wsRequest.mandatoryParamAsStrings(PARAM_QUALIFIERS);
+    validateQualifiers(qualifiers);
+
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      ComponentQuery query = buildQuery(wsRequest, qualifiers);
+      Paging paging = buildPaging(dbSession, wsRequest, query);
+      List<ComponentDto> components = searchComponents(dbSession, query, paging);
+      WsSearchResponse response = buildResponse(components, paging);
+      writeProtobuf(response, wsRequest, wsResponse);
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+  }
+
+  private List<ComponentDto> searchComponents(DbSession dbSession, ComponentQuery query, Paging paging) {
+    return dbClient.componentDao().selectByQuery(
+      dbSession,
+      query,
+      paging.offset(),
+      paging.pageSize());
+  }
+
+  private WsSearchResponse buildResponse(List<ComponentDto> components, Paging paging) {
+    WsSearchResponse.Builder responseBuilder = WsSearchResponse.newBuilder();
+    responseBuilder.getPagingBuilder()
+      .setPageIndex(paging.pageIndex())
+      .setPageSize(paging.pageSize())
+      .setTotal(paging.total())
+      .build();
+
+    responseBuilder.addAllComponents(
+      from(components)
+        .transform(ComponentDToComponentResponseFunction.INSTANCE));
+
+    return responseBuilder.build();
+  }
+
+  private Paging buildPaging(DbSession dbSession, Request wsRequest, ComponentQuery query) {
+    int total = dbClient.componentDao().countByQuery(dbSession, query);
+    return Paging.forPageIndex(wsRequest.mandatoryParamAsInt(Param.PAGE))
+      .withPageSize(wsRequest.mandatoryParamAsInt(Param.PAGE_SIZE))
+      .andTotal(total);
+  }
+
+  private ComponentQuery buildQuery(Request wsRequest, List<String> qualifiers) {
+    return new ComponentQuery(
+      dbClient.getDatabase(),
+      wsRequest.param(Param.TEXT_QUERY),
+      qualifiers.toArray(new String[qualifiers.size()]));
+  }
+
+  private void validateQualifiers(List<String> qualifiers) {
+    Set<String> possibleQualifiers = allQualifiers();
+    for (String qualifier : qualifiers) {
+      checkRequest(possibleQualifiers.contains(qualifier),
+        format("The '%s' parameter must be one of %s. '%s' was passed.", PARAM_QUALIFIER, possibleQualifiers, qualifier));
+    }
+  }
+
+  private Set<String> allQualifiers() {
+    return from(resourceTypes.getAll())
+      .transform(RESOURCE_TYPE_TO_QUALIFIER)
+      .toSortedSet(natural());
+  }
+
+  private String buildQualifiersDescription() {
+    StringBuilder description = new StringBuilder();
+    description.append("<ul>");
+    String qualifierPattern = "<li>%s - %s</li>";
+    for (String qualifier : allQualifiers()) {
+      description.append(format(qualifierPattern, qualifier, i18n(qualifier)));
+    }
+    description.append("</ul>");
+
+    return description.toString();
+  }
+
+  private String i18n(String qualifier) {
+    return i18n.message(userSession.locale(), QUALIFIER_PROPERTY_PREFIX + qualifier, "");
+  }
+
+  private enum ComponentDToComponentResponseFunction implements Function<ComponentDto, WsSearchResponse.Component> {
+    INSTANCE;
 
+    @Override
+    public WsSearchResponse.Component apply(@Nonnull ComponentDto dto) {
+      return WsSearchResponse.Component.newBuilder()
+        .setId(dto.uuid())
+        .setKey(dto.key())
+        .setName(dto.name())
+        .setQualifier(dto.qualifier())
+        .build();
+    }
   }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/WsComponentsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/WsComponentsParameters.java
new file mode 100644 (file)
index 0000000..f33b3ad
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.component.ws;
+
+class WsComponentsParameters {
+  private WsComponentsParameters() {
+    // static utility class
+  }
+
+  static final String PARAM_QUALIFIERS = "qualifiers";
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/ResourceTypeToQualifier.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/ResourceTypeToQualifier.java
deleted file mode 100644 (file)
index 8c850cb..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.server.permission.ws;
-
-import com.google.common.base.Function;
-import javax.annotation.Nonnull;
-import org.sonar.api.resources.ResourceType;
-
-public class ResourceTypeToQualifier {
-
-  public static final Function<ResourceType, String> RESOURCE_TYPE_TO_QUALIFIER = Singleton.INSTANCE;
-
-  private enum Singleton implements Function<ResourceType, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull ResourceType resourceType) {
-      return resourceType.getQualifier();
-    }
-  }
-}
index a8489228ae691b776db4bf01536e0a15dab59889..bbdca2994dd0a5bc66412cb34636991505f8a7ca 100644 (file)
@@ -44,7 +44,7 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE;
 import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
 import static org.sonar.api.utils.Paging.forPageIndex;
-import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER;
+import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER;
 import static org.sonar.server.permission.ws.SearchProjectPermissionsData.newBuilder;
 
 public class SearchProjectPermissionsDataLoader {
index 2ee49517dee278efc919443b550d79ccfcf55cea..f309355ff53c0914db0b0b7c314c3d5d051eb9a7 100644 (file)
@@ -32,7 +32,7 @@ import static com.google.common.collect.FluentIterable.from;
 import static com.google.common.collect.Ordering.natural;
 import static org.sonar.server.permission.DefaultPermissionTemplates.DEFAULT_TEMPLATE_PROPERTY;
 import static org.sonar.server.permission.DefaultPermissionTemplates.defaultRootQualifierTemplateProperty;
-import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER;
+import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER;
 
 public class DefaultPermissionTemplateFinder {
   private final Settings settings;
index d2ffbb4d67105a53e3405ad3ed7fd2b7faeefd6c..a8c8e7b664bf7eda139da78bd1f4bc43bc61da4d 100644 (file)
@@ -44,7 +44,7 @@ import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobal
 import static org.sonar.server.permission.ws.WsPermissionParameters.PARAM_QUALIFIER;
 import static org.sonar.server.permission.ws.WsPermissionParameters.createTemplateParameters;
 import static org.sonar.server.permission.ws.PermissionRequestValidator.validateQualifier;
-import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER;
+import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER;
 
 public class SetDefaultTemplateAction implements PermissionsWsAction {
   private final DbClient dbClient;
index e0b8f64d16ef672cd3c0d603847c79f48f8b8080..d0922654bd22bdda6d110a690976d8cdbc9d0f10 100644 (file)
@@ -58,10 +58,7 @@ import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.ComponentService;
 import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
-import org.sonar.server.component.ws.ComponentsWs;
-import org.sonar.server.component.ws.EventsWs;
-import org.sonar.server.component.ws.ResourcesWs;
-import org.sonar.server.component.ws.SearchViewComponentsAction;
+import org.sonar.server.component.ws.ComponentsWsModule;
 import org.sonar.server.computation.CeModule;
 import org.sonar.server.computation.container.ReportProcessingModule;
 import org.sonar.server.computation.queue.CeQueueModule;
@@ -338,16 +335,16 @@ public class PlatformLevel4 extends PlatformLevel {
       IndexDefinitions.class,
       IndexCreator.class,
 
-      // Activity
+    // Activity
       ActivityService.class,
       ActivityIndexDefinition.class,
       ActivityIndexer.class,
       ActivityIndex.class,
 
-      // batch
+    // batch
       BatchWsModule.class,
 
-      // Dashboard
+    // Dashboard
       DashboardsWs.class,
       org.sonar.server.dashboard.ws.ShowAction.class,
       ProjectDefaultDashboard.class,
@@ -385,12 +382,12 @@ public class PlatformLevel4 extends PlatformLevel {
       ProjectIssueFilterWidget.class,
       IssueTagCloudWidget.class,
 
-      // update center
+    // update center
       UpdateCenterClient.class,
       UpdateCenterMatrixFactory.class,
       UpdateCenterWs.class,
 
-      // quality profile
+    // quality profile
       XMLProfileParser.class,
       XMLProfileSerializer.class,
       AnnotationProfileParser.class,
@@ -434,7 +431,7 @@ public class PlatformLevel4 extends PlatformLevel {
       QProfileReset.class,
       RubyQProfileActivityService.class,
 
-      // rule
+    // rule
       AnnotationRuleParser.class,
       XMLRuleParser.class,
       DefaultRuleFinder.class,
@@ -462,17 +459,17 @@ public class PlatformLevel4 extends PlatformLevel {
       RepositoriesAction.class,
       org.sonar.server.rule.ws.AppAction.class,
 
-      // languages
+    // languages
       Languages.class,
       LanguageWs.class,
       org.sonar.server.language.ws.ListAction.class,
 
-      // activity
+    // activity
       ActivitiesWs.class,
       org.sonar.server.activity.ws.SearchAction.class,
       ActivityMapping.class,
 
-      // measure
+    // measure
       MeasureFilterFactory.class,
       MeasureFilterExecutor.class,
       MeasureFilterEngine.class,
@@ -484,14 +481,14 @@ public class PlatformLevel4 extends PlatformLevel {
       DefaultMetricFinder.class,
       TimeMachineWs.class,
 
-      // quality gates
+    // quality gates
       QualityGateDao.class,
       QualityGateConditionDao.class,
       QualityGates.class,
       ProjectQgateAssociationDao.class,
       QgateProjectFinder.class,
 
-      org.sonar.server.qualitygate.ws.ListAction.class,
+    org.sonar.server.qualitygate.ws.ListAction.class,
       org.sonar.server.qualitygate.ws.SearchAction.class,
       org.sonar.server.qualitygate.ws.ShowAction.class,
       org.sonar.server.qualitygate.ws.CreateAction.class,
@@ -508,17 +505,17 @@ public class PlatformLevel4 extends PlatformLevel {
       org.sonar.server.qualitygate.ws.AppAction.class,
       QGatesWs.class,
 
-      // web services
+    // web services
       WebServiceEngine.class,
       ListingWs.class,
 
-      // localization
+    // localization
       L10nWs.class,
 
-      // authentication
+    // authentication
       AuthenticationWs.class,
 
-      // users
+    // users
       SecurityRealmFactory.class,
       DeprecatedUserFinder.class,
       NewUserNotifier.class,
@@ -540,39 +537,35 @@ public class PlatformLevel4 extends PlatformLevel {
       UserIndex.class,
       UserUpdater.class,
 
-      // groups
+    // groups
       GroupMembershipService.class,
       GroupMembershipFinder.class,
       UserGroupsModule.class,
 
-      // permissions
+    // permissions
       PermissionRepository.class,
       PermissionService.class,
       PermissionUpdater.class,
       PermissionFinder.class,
       PermissionsWsModule.class,
 
-      // components
+    // components
       ProjectsWsModule.class,
+      ComponentsWsModule.class,
       DefaultComponentFinder.class,
       DefaultRubyComponentService.class,
       ComponentService.class,
       ComponentFinder.class,
-      ResourcesWs.class,
-      ComponentsWs.class,
-      org.sonar.server.component.ws.AppAction.class,
-      SearchViewComponentsAction.class,
-      EventsWs.class,
       NewAlerts.class,
       NewAlerts.newMetadata(),
       ComponentCleanerService.class,
 
-      // views
+    // views
       ViewIndexDefinition.class,
       ViewIndexer.class,
       ViewIndex.class,
 
-      // issues
+    // issues
       IssueIndexDefinition.class,
       IssueIndexer.class,
       IssueAuthorizationIndexer.class,
@@ -606,13 +599,13 @@ public class PlatformLevel4 extends PlatformLevel {
       EmailNotificationChannel.class,
       AlertsEmailTemplate.class,
 
-      IssueFilterWsModule.class,
+    IssueFilterWsModule.class,
 
-      // action plan
+    // action plan
       ActionPlanWs.class,
       ActionPlanService.class,
 
-      // issues actions
+    // issues actions
       AssignAction.class,
       PlanAction.class,
       SetSeverityAction.class,
@@ -621,7 +614,7 @@ public class PlatformLevel4 extends PlatformLevel {
       AddTagsAction.class,
       RemoveTagsAction.class,
 
-      // technical debt
+    // technical debt
       DebtModelService.class,
       DebtModelOperations.class,
       DebtModelLookup.class,
@@ -631,7 +624,7 @@ public class PlatformLevel4 extends PlatformLevel {
       DebtRulesXMLImporter.class,
       DebtCharacteristicsXMLImporter.class,
 
-      // source
+    // source
       HtmlSourceDecorator.class,
       SourceService.class,
       SourcesWs.class,
@@ -642,23 +635,23 @@ public class PlatformLevel4 extends PlatformLevel {
       IndexAction.class,
       ScmAction.class,
 
-      // Duplications
+    // Duplications
       DuplicationsParser.class,
       DuplicationsWs.class,
       DuplicationsJsonWriter.class,
       org.sonar.server.duplication.ws.ShowAction.class,
 
-      // text
+    // text
       MacroInterpreter.class,
       RubyTextService.class,
 
-      // Notifications
+    // Notifications
       EmailSettings.class,
       NotificationService.class,
       NotificationCenter.class,
       DefaultNotificationManager.class,
 
-      // Tests
+    // Tests
       CoverageService.class,
       TestsWs.class,
       CoveredFilesAction.class,
@@ -667,12 +660,12 @@ public class PlatformLevel4 extends PlatformLevel {
       TestIndex.class,
       TestIndexer.class,
 
-      // Properties
+    // Properties
       PropertiesWs.class,
 
-      TypeValidationModule.class,
+    TypeValidationModule.class,
 
-      // System
+    // System
       RestartAction.class,
       InfoAction.class,
       UpgradesAction.class,
@@ -687,7 +680,7 @@ public class PlatformLevel4 extends PlatformLevel {
       MigrateDbAction.class,
       DbMigrationStatusAction.class,
 
-      // Plugins WS
+    // Plugins WS
       PluginWSCommons.class,
       PluginUpdateAggregator.class,
       InstalledAction.class,
@@ -700,18 +693,18 @@ public class PlatformLevel4 extends PlatformLevel {
       CancelAllAction.class,
       PluginsWs.class,
 
-      // Compute engine
+    // Compute engine
       CeModule.class,
       CeQueueModule.class,
       CeTaskProcessorModule.class,
       CeWsModule.class,
       ReportProcessingModule.class,
 
-      // Views plugin
+    // Views plugin
       ViewsBootstrap.class,
       ViewsStopper.class,
 
-      // UI
+    // UI
       GlobalNavigationAction.class,
       SettingsNavigationAction.class,
       ComponentNavigationAction.class,
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/search-components-example.json b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/search-components-example.json
new file mode 100644 (file)
index 0000000..80a9ad9
--- /dev/null
@@ -0,0 +1,33 @@
+{
+  "paging": {
+    "pageIndex": 1,
+    "pageSize": 100,
+    "total": 4
+  },
+  "components": [
+    {
+      "id": "directory-uuid",
+      "key": "directory-key",
+      "qualifier": "DIR",
+      "name": "Directory Name"
+    },
+    {
+      "id": "file-uuid",
+      "key": "file-key",
+      "qualifier": "FIL",
+      "name": "File Name"
+    },
+    {
+      "id": "module-uuid",
+      "key": "module-key",
+      "qualifier": "BRC",
+      "name": "Module Name"
+    },
+    {
+      "id": "project-uuid",
+      "key": "project-key",
+      "qualifier": "TRK",
+      "name": "Project Name"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java
new file mode 100644 (file)
index 0000000..d68d694
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.component.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ComponentsWsModuleTest {
+  @Test
+  public void verify_count_of_added_components() {
+    ComponentContainer container = new ComponentContainer();
+    new ComponentsWsModule().configure(container);
+    assertThat(container.size()).isEqualTo(8);
+  }
+}
index 0159481fafb030b84355e8855b737f95f7024c8f..6020baaa32ece53e079b798094f735f3765b2c0c 100644 (file)
@@ -24,6 +24,7 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.i18n.I18n;
+import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.Durations;
@@ -45,8 +46,9 @@ public class ComponentsWsTest {
   public void setUp() {
     WsTester tester = new WsTester(new ComponentsWs(
       new AppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class), userSessionRule, mock(ComponentFinder.class)),
-      new SearchViewComponentsAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)))
-      );
+      new SearchViewComponentsAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
+      new SearchAction(mock(org.sonar.db.DbClient.class), mock(ResourceTypes.class), mock(I18n.class), userSessionRule)
+      ));
     controller = tester.controller("api/components");
   }
 
@@ -55,7 +57,7 @@ public class ComponentsWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
     assertThat(controller.since()).isEqualTo("4.2");
-    assertThat(controller.actions()).hasSize(3);
+    assertThat(controller.actions()).hasSize(4);
   }
 
   @Test
@@ -80,8 +82,9 @@ public class ComponentsWsTest {
   }
 
   @Test
-  public void define_search_action() {
+  public void define_search_view_components_action() {
     WebService.Action action = controller.action("search_view_components");
+
     assertThat(action).isNotNull();
     assertThat(action.isInternal()).isTrue();
     assertThat(action.isPost()).isFalse();
@@ -89,4 +92,16 @@ public class ComponentsWsTest {
     assertThat(action.params()).hasSize(4);
   }
 
+  @Test
+  public void define_search_action() {
+    WebService.Action action = controller.action("search");
+
+    assertThat(action).isNotNull();
+    assertThat(action.param("qualifiers").isRequired()).isTrue();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.isInternal()).isTrue();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.since()).isEqualTo("5.2");
+  }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java
new file mode 100644 (file)
index 0000000..00a02a1
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.base.Joiner;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.ResourceType;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.System2;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.i18n.I18nRule;
+import org.sonar.server.plugins.MimeTypes;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.WsComponents.WsSearchResponse;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.server.component.ws.WsComponentsParameters.PARAM_QUALIFIERS;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class SearchActionTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+  ComponentDbTester componentDb = new ComponentDbTester(db);
+  I18nRule i18n = new I18nRule();
+
+  WsActionTester ws;
+  ResourceTypes resourceTypes;
+
+  @Before
+  public void setUp() {
+    userSession.login().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+    resourceTypes = mock(ResourceTypes.class);
+    when(resourceTypes.getAll()).thenReturn(resourceTypes());
+
+    ws = new WsActionTester(new SearchAction(db.getDbClient(), resourceTypes, i18n, userSession));
+
+  }
+
+  @Test
+  public void search_json_example() {
+    componentDb.insertComponent(newView());
+    ComponentDto project = componentDb.insertComponent(
+      newProjectDto("project-uuid")
+        .setName("Project Name")
+        .setKey("project-key")
+      );
+    ComponentDto module = componentDb.insertComponent(
+      newModuleDto("module-uuid", project)
+        .setName("Module Name")
+        .setKey("module-key")
+      );
+    componentDb.insertComponent(
+      newDirectory(module, "path/to/directoy")
+        .setUuid("directory-uuid")
+        .setKey("directory-key")
+        .setName("Directory Name")
+      );
+    componentDb.insertComponent(
+      newFileDto(module, "file-uuid")
+        .setKey("file-key")
+        .setName("File Name")
+      );
+    db.commit();
+
+    String response = newRequest(Qualifiers.PROJECT, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.FILE)
+      .setMediaType(MimeTypes.JSON)
+      .execute()
+      .getInput();
+
+    assertJson(response).isSimilarTo(getClass().getResource("search-components-example.json"));
+  }
+
+  @Test
+  public void search_with_pagination() throws IOException {
+    for (int i = 1; i <= 9; i++) {
+      componentDb.insertComponent(
+        newProjectDto("project-uuid-" + i)
+          .setName("Project Name " + i));
+    }
+    db.commit();
+
+    InputStream responseStream = newRequest(Qualifiers.PROJECT)
+      .setParam(Param.PAGE, "2")
+      .setParam(Param.PAGE_SIZE, "3")
+      .execute()
+      .getInputStream();
+    WsSearchResponse response = WsSearchResponse.parseFrom(responseStream);
+
+    assertThat(response.getComponentsCount()).isEqualTo(3);
+    assertThat(response.getComponentsList()).extracting("id").containsExactly("project-uuid-4", "project-uuid-5", "project-uuid-6");
+  }
+
+  @Test
+  public void search_with_key_query() throws IOException {
+    componentDb.insertComponent(newProjectDto().setKey("project-_%-key"));
+    componentDb.insertComponent(newProjectDto().setKey("project-key-without-escaped-characters"));
+    db.commit();
+
+    InputStream responseStream = newRequest(Qualifiers.PROJECT)
+      .setParam(Param.TEXT_QUERY, "project-_%")
+      .execute().getInputStream();
+    WsSearchResponse response = WsSearchResponse.parseFrom(responseStream);
+
+    assertThat(response.getComponentsCount()).isEqualTo(1);
+    assertThat(response.getComponentsList()).extracting("key").containsExactly("project-_%-key");
+  }
+
+  @Test
+  public void fail_if_unknown_qualifier_provided() {
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("The 'qualifier' parameter must be one of [BRC, DIR, FIL, TRK]. 'Unknown-Qualifier' was passed.");
+
+    newRequest("Unknown-Qualifier").execute();
+  }
+
+  @Test
+  public void fail_if_not_logged_in() {
+    expectedException.expect(UnauthorizedException.class);
+    userSession.anonymous();
+
+    newRequest(Qualifiers.PROJECT).execute();
+  }
+
+  @Test
+  public void fail_if_insufficient_privileges() {
+    expectedException.expect(ForbiddenException.class);
+    userSession.login().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+
+    newRequest(Qualifiers.PROJECT).execute();
+  }
+
+  private TestRequest newRequest(String... qualifiers) {
+    return ws.newRequest()
+      .setMediaType(MimeTypes.PROTOBUF)
+      .setParam(PARAM_QUALIFIERS, Joiner.on(",").join(qualifiers));
+  }
+
+  private static List<ResourceType> resourceTypes() {
+    return asList(
+      ResourceType.builder(Qualifiers.PROJECT).build(),
+      ResourceType.builder(Qualifiers.MODULE).build(),
+      ResourceType.builder(Qualifiers.DIRECTORY).build(),
+      ResourceType.builder(Qualifiers.FILE).build());
+  }
+}
diff --git a/sonar-ws/src/main/gen-java/org/sonarqube/ws/WsComponents.java b/sonar-ws/src/main/gen-java/org/sonarqube/ws/WsComponents.java
new file mode 100644 (file)
index 0000000..67a2de0
--- /dev/null
@@ -0,0 +1,1884 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: ws-components.proto
+
+package org.sonarqube.ws;
+
+public final class WsComponents {
+  private WsComponents() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface WsSearchResponseOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:sonarqube.ws.component.WsSearchResponse)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    boolean hasPaging();
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    org.sonarqube.ws.Common.Paging getPaging();
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    org.sonarqube.ws.Common.PagingOrBuilder getPagingOrBuilder();
+
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component> 
+        getComponentsList();
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    org.sonarqube.ws.WsComponents.WsSearchResponse.Component getComponents(int index);
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    int getComponentsCount();
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    java.util.List<? extends org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder> 
+        getComponentsOrBuilderList();
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder getComponentsOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code sonarqube.ws.component.WsSearchResponse}
+   *
+   * <pre>
+   * WS api/components/search
+   * </pre>
+   */
+  public  static final class WsSearchResponse extends
+      com.google.protobuf.GeneratedMessage implements
+      // @@protoc_insertion_point(message_implements:sonarqube.ws.component.WsSearchResponse)
+      WsSearchResponseOrBuilder {
+    // Use WsSearchResponse.newBuilder() to construct.
+    private WsSearchResponse(com.google.protobuf.GeneratedMessage.Builder builder) {
+      super(builder);
+    }
+    private WsSearchResponse() {
+      components_ = java.util.Collections.emptyList();
+    }
+
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+    getUnknownFields() {
+      return this.unknownFields;
+    }
+    private WsSearchResponse(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry) {
+      this();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              org.sonarqube.ws.Common.Paging.Builder subBuilder = null;
+              if (((bitField0_ & 0x00000001) == 0x00000001)) {
+                subBuilder = paging_.toBuilder();
+              }
+              paging_ = input.readMessage(org.sonarqube.ws.Common.Paging.PARSER, extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(paging_);
+                paging_ = subBuilder.buildPartial();
+              }
+              bitField0_ |= 0x00000001;
+              break;
+            }
+            case 18: {
+              if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) {
+                components_ = new java.util.ArrayList<org.sonarqube.ws.WsComponents.WsSearchResponse.Component>();
+                mutable_bitField0_ |= 0x00000002;
+              }
+              components_.add(input.readMessage(org.sonarqube.ws.WsComponents.WsSearchResponse.Component.PARSER, extensionRegistry));
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw new RuntimeException(e.setUnfinishedMessage(this));
+      } catch (java.io.IOException e) {
+        throw new RuntimeException(
+            new com.google.protobuf.InvalidProtocolBufferException(
+                e.getMessage()).setUnfinishedMessage(this));
+      } finally {
+        if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) {
+          components_ = java.util.Collections.unmodifiableList(components_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.sonarqube.ws.WsComponents.WsSearchResponse.class, org.sonarqube.ws.WsComponents.WsSearchResponse.Builder.class);
+    }
+
+    public interface ComponentOrBuilder extends
+        // @@protoc_insertion_point(interface_extends:sonarqube.ws.component.WsSearchResponse.Component)
+        com.google.protobuf.MessageOrBuilder {
+
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      boolean hasId();
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      java.lang.String getId();
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      com.google.protobuf.ByteString
+          getIdBytes();
+
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      boolean hasKey();
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      java.lang.String getKey();
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      com.google.protobuf.ByteString
+          getKeyBytes();
+
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      boolean hasQualifier();
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      java.lang.String getQualifier();
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      com.google.protobuf.ByteString
+          getQualifierBytes();
+
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      boolean hasName();
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      java.lang.String getName();
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      com.google.protobuf.ByteString
+          getNameBytes();
+    }
+    /**
+     * Protobuf type {@code sonarqube.ws.component.WsSearchResponse.Component}
+     */
+    public  static final class Component extends
+        com.google.protobuf.GeneratedMessage implements
+        // @@protoc_insertion_point(message_implements:sonarqube.ws.component.WsSearchResponse.Component)
+        ComponentOrBuilder {
+      // Use Component.newBuilder() to construct.
+      private Component(com.google.protobuf.GeneratedMessage.Builder builder) {
+        super(builder);
+      }
+      private Component() {
+        id_ = "";
+        key_ = "";
+        qualifier_ = "";
+        name_ = "";
+      }
+
+      @java.lang.Override
+      public final com.google.protobuf.UnknownFieldSet
+      getUnknownFields() {
+        return this.unknownFields;
+      }
+      private Component(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry) {
+        this();
+        int mutable_bitField0_ = 0;
+        com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+            com.google.protobuf.UnknownFieldSet.newBuilder();
+        try {
+          boolean done = false;
+          while (!done) {
+            int tag = input.readTag();
+            switch (tag) {
+              case 0:
+                done = true;
+                break;
+              default: {
+                if (!parseUnknownField(input, unknownFields,
+                                       extensionRegistry, tag)) {
+                  done = true;
+                }
+                break;
+              }
+              case 10: {
+                com.google.protobuf.ByteString bs = input.readBytes();
+                bitField0_ |= 0x00000001;
+                id_ = bs;
+                break;
+              }
+              case 18: {
+                com.google.protobuf.ByteString bs = input.readBytes();
+                bitField0_ |= 0x00000002;
+                key_ = bs;
+                break;
+              }
+              case 26: {
+                com.google.protobuf.ByteString bs = input.readBytes();
+                bitField0_ |= 0x00000004;
+                qualifier_ = bs;
+                break;
+              }
+              case 34: {
+                com.google.protobuf.ByteString bs = input.readBytes();
+                bitField0_ |= 0x00000008;
+                name_ = bs;
+                break;
+              }
+            }
+          }
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw new RuntimeException(e.setUnfinishedMessage(this));
+        } catch (java.io.IOException e) {
+          throw new RuntimeException(
+              new com.google.protobuf.InvalidProtocolBufferException(
+                  e.getMessage()).setUnfinishedMessage(this));
+        } finally {
+          this.unknownFields = unknownFields.build();
+          makeExtensionsImmutable();
+        }
+      }
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_Component_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.sonarqube.ws.WsComponents.WsSearchResponse.Component.class, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder.class);
+      }
+
+      private int bitField0_;
+      public static final int ID_FIELD_NUMBER = 1;
+      private volatile java.lang.Object id_;
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      public boolean hasId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      public java.lang.String getId() {
+        java.lang.Object ref = id_;
+        if (ref instanceof java.lang.String) {
+          return (java.lang.String) ref;
+        } else {
+          com.google.protobuf.ByteString bs = 
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          if (bs.isValidUtf8()) {
+            id_ = s;
+          }
+          return s;
+        }
+      }
+      /**
+       * <code>optional string id = 1;</code>
+       */
+      public com.google.protobuf.ByteString
+          getIdBytes() {
+        java.lang.Object ref = id_;
+        if (ref instanceof java.lang.String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          id_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+
+      public static final int KEY_FIELD_NUMBER = 2;
+      private volatile java.lang.Object key_;
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      public boolean hasKey() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      public java.lang.String getKey() {
+        java.lang.Object ref = key_;
+        if (ref instanceof java.lang.String) {
+          return (java.lang.String) ref;
+        } else {
+          com.google.protobuf.ByteString bs = 
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          if (bs.isValidUtf8()) {
+            key_ = s;
+          }
+          return s;
+        }
+      }
+      /**
+       * <code>optional string key = 2;</code>
+       */
+      public com.google.protobuf.ByteString
+          getKeyBytes() {
+        java.lang.Object ref = key_;
+        if (ref instanceof java.lang.String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          key_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+
+      public static final int QUALIFIER_FIELD_NUMBER = 3;
+      private volatile java.lang.Object qualifier_;
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      public boolean hasQualifier() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      public java.lang.String getQualifier() {
+        java.lang.Object ref = qualifier_;
+        if (ref instanceof java.lang.String) {
+          return (java.lang.String) ref;
+        } else {
+          com.google.protobuf.ByteString bs = 
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          if (bs.isValidUtf8()) {
+            qualifier_ = s;
+          }
+          return s;
+        }
+      }
+      /**
+       * <code>optional string qualifier = 3;</code>
+       */
+      public com.google.protobuf.ByteString
+          getQualifierBytes() {
+        java.lang.Object ref = qualifier_;
+        if (ref instanceof java.lang.String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          qualifier_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+
+      public static final int NAME_FIELD_NUMBER = 4;
+      private volatile java.lang.Object name_;
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      public boolean hasName() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      public java.lang.String getName() {
+        java.lang.Object ref = name_;
+        if (ref instanceof java.lang.String) {
+          return (java.lang.String) ref;
+        } else {
+          com.google.protobuf.ByteString bs = 
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          if (bs.isValidUtf8()) {
+            name_ = s;
+          }
+          return s;
+        }
+      }
+      /**
+       * <code>optional string name = 4;</code>
+       */
+      public com.google.protobuf.ByteString
+          getNameBytes() {
+        java.lang.Object ref = name_;
+        if (ref instanceof java.lang.String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          name_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+
+      private byte memoizedIsInitialized = -1;
+      public final boolean isInitialized() {
+        byte isInitialized = memoizedIsInitialized;
+        if (isInitialized == 1) return true;
+        if (isInitialized == 0) return false;
+
+        memoizedIsInitialized = 1;
+        return true;
+      }
+
+      public void writeTo(com.google.protobuf.CodedOutputStream output)
+                          throws java.io.IOException {
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          output.writeBytes(1, getIdBytes());
+        }
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          output.writeBytes(2, getKeyBytes());
+        }
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          output.writeBytes(3, getQualifierBytes());
+        }
+        if (((bitField0_ & 0x00000008) == 0x00000008)) {
+          output.writeBytes(4, getNameBytes());
+        }
+        unknownFields.writeTo(output);
+      }
+
+      private int memoizedSerializedSize = -1;
+      public int getSerializedSize() {
+        int size = memoizedSerializedSize;
+        if (size != -1) return size;
+
+        size = 0;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          size += com.google.protobuf.CodedOutputStream
+            .computeBytesSize(1, getIdBytes());
+        }
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          size += com.google.protobuf.CodedOutputStream
+            .computeBytesSize(2, getKeyBytes());
+        }
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          size += com.google.protobuf.CodedOutputStream
+            .computeBytesSize(3, getQualifierBytes());
+        }
+        if (((bitField0_ & 0x00000008) == 0x00000008)) {
+          size += com.google.protobuf.CodedOutputStream
+            .computeBytesSize(4, getNameBytes());
+        }
+        size += unknownFields.getSerializedSize();
+        memoizedSerializedSize = size;
+        return size;
+      }
+
+      private static final long serialVersionUID = 0L;
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          com.google.protobuf.ByteString data)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return PARSER.parseFrom(data);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          com.google.protobuf.ByteString data,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return PARSER.parseFrom(data, extensionRegistry);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(byte[] data)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return PARSER.parseFrom(data);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          byte[] data,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return PARSER.parseFrom(data, extensionRegistry);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(java.io.InputStream input)
+          throws java.io.IOException {
+        return PARSER.parseFrom(input);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          java.io.InputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        return PARSER.parseFrom(input, extensionRegistry);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseDelimitedFrom(java.io.InputStream input)
+          throws java.io.IOException {
+        return PARSER.parseDelimitedFrom(input);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseDelimitedFrom(
+          java.io.InputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        return PARSER.parseDelimitedFrom(input, extensionRegistry);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          com.google.protobuf.CodedInputStream input)
+          throws java.io.IOException {
+        return PARSER.parseFrom(input);
+      }
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component parseFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        return PARSER.parseFrom(input, extensionRegistry);
+      }
+
+      public Builder newBuilderForType() { return newBuilder(); }
+      public static Builder newBuilder() {
+        return DEFAULT_INSTANCE.toBuilder();
+      }
+      public static Builder newBuilder(org.sonarqube.ws.WsComponents.WsSearchResponse.Component prototype) {
+        return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+      }
+      public Builder toBuilder() {
+        return this == DEFAULT_INSTANCE
+            ? new Builder() : new Builder().mergeFrom(this);
+      }
+
+      @java.lang.Override
+      protected Builder newBuilderForType(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        Builder builder = new Builder(parent);
+        return builder;
+      }
+      /**
+       * Protobuf type {@code sonarqube.ws.component.WsSearchResponse.Component}
+       */
+      public static final class Builder extends
+          com.google.protobuf.GeneratedMessage.Builder<Builder> implements
+          // @@protoc_insertion_point(builder_implements:sonarqube.ws.component.WsSearchResponse.Component)
+          org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder {
+        public static final com.google.protobuf.Descriptors.Descriptor
+            getDescriptor() {
+          return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor;
+        }
+
+        protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+            internalGetFieldAccessorTable() {
+          return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_Component_fieldAccessorTable
+              .ensureFieldAccessorsInitialized(
+                  org.sonarqube.ws.WsComponents.WsSearchResponse.Component.class, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder.class);
+        }
+
+        // Construct using org.sonarqube.ws.WsComponents.WsSearchResponse.Component.newBuilder()
+        private Builder() {
+          maybeForceBuilderInitialization();
+        }
+
+        private Builder(
+            com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+          super(parent);
+          maybeForceBuilderInitialization();
+        }
+        private void maybeForceBuilderInitialization() {
+          if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          }
+        }
+        public Builder clear() {
+          super.clear();
+          id_ = "";
+          bitField0_ = (bitField0_ & ~0x00000001);
+          key_ = "";
+          bitField0_ = (bitField0_ & ~0x00000002);
+          qualifier_ = "";
+          bitField0_ = (bitField0_ & ~0x00000004);
+          name_ = "";
+          bitField0_ = (bitField0_ & ~0x00000008);
+          return this;
+        }
+
+        public com.google.protobuf.Descriptors.Descriptor
+            getDescriptorForType() {
+          return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor;
+        }
+
+        public org.sonarqube.ws.WsComponents.WsSearchResponse.Component getDefaultInstanceForType() {
+          return org.sonarqube.ws.WsComponents.WsSearchResponse.Component.getDefaultInstance();
+        }
+
+        public org.sonarqube.ws.WsComponents.WsSearchResponse.Component build() {
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component result = buildPartial();
+          if (!result.isInitialized()) {
+            throw newUninitializedMessageException(result);
+          }
+          return result;
+        }
+
+        public org.sonarqube.ws.WsComponents.WsSearchResponse.Component buildPartial() {
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component result = new org.sonarqube.ws.WsComponents.WsSearchResponse.Component(this);
+          int from_bitField0_ = bitField0_;
+          int to_bitField0_ = 0;
+          if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+            to_bitField0_ |= 0x00000001;
+          }
+          result.id_ = id_;
+          if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+            to_bitField0_ |= 0x00000002;
+          }
+          result.key_ = key_;
+          if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+            to_bitField0_ |= 0x00000004;
+          }
+          result.qualifier_ = qualifier_;
+          if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+            to_bitField0_ |= 0x00000008;
+          }
+          result.name_ = name_;
+          result.bitField0_ = to_bitField0_;
+          onBuilt();
+          return result;
+        }
+
+        public Builder mergeFrom(com.google.protobuf.Message other) {
+          if (other instanceof org.sonarqube.ws.WsComponents.WsSearchResponse.Component) {
+            return mergeFrom((org.sonarqube.ws.WsComponents.WsSearchResponse.Component)other);
+          } else {
+            super.mergeFrom(other);
+            return this;
+          }
+        }
+
+        public Builder mergeFrom(org.sonarqube.ws.WsComponents.WsSearchResponse.Component other) {
+          if (other == org.sonarqube.ws.WsComponents.WsSearchResponse.Component.getDefaultInstance()) return this;
+          if (other.hasId()) {
+            bitField0_ |= 0x00000001;
+            id_ = other.id_;
+            onChanged();
+          }
+          if (other.hasKey()) {
+            bitField0_ |= 0x00000002;
+            key_ = other.key_;
+            onChanged();
+          }
+          if (other.hasQualifier()) {
+            bitField0_ |= 0x00000004;
+            qualifier_ = other.qualifier_;
+            onChanged();
+          }
+          if (other.hasName()) {
+            bitField0_ |= 0x00000008;
+            name_ = other.name_;
+            onChanged();
+          }
+          this.mergeUnknownFields(other.unknownFields);
+          onChanged();
+          return this;
+        }
+
+        public final boolean isInitialized() {
+          return true;
+        }
+
+        public Builder mergeFrom(
+            com.google.protobuf.CodedInputStream input,
+            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+            throws java.io.IOException {
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component parsedMessage = null;
+          try {
+            parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+          } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+            parsedMessage = (org.sonarqube.ws.WsComponents.WsSearchResponse.Component) e.getUnfinishedMessage();
+            throw e;
+          } finally {
+            if (parsedMessage != null) {
+              mergeFrom(parsedMessage);
+            }
+          }
+          return this;
+        }
+        private int bitField0_;
+
+        private java.lang.Object id_ = "";
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public boolean hasId() {
+          return ((bitField0_ & 0x00000001) == 0x00000001);
+        }
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public java.lang.String getId() {
+          java.lang.Object ref = id_;
+          if (!(ref instanceof java.lang.String)) {
+            com.google.protobuf.ByteString bs =
+                (com.google.protobuf.ByteString) ref;
+            java.lang.String s = bs.toStringUtf8();
+            if (bs.isValidUtf8()) {
+              id_ = s;
+            }
+            return s;
+          } else {
+            return (java.lang.String) ref;
+          }
+        }
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public com.google.protobuf.ByteString
+            getIdBytes() {
+          java.lang.Object ref = id_;
+          if (ref instanceof String) {
+            com.google.protobuf.ByteString b = 
+                com.google.protobuf.ByteString.copyFromUtf8(
+                    (java.lang.String) ref);
+            id_ = b;
+            return b;
+          } else {
+            return (com.google.protobuf.ByteString) ref;
+          }
+        }
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public Builder setId(
+            java.lang.String value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+          id_ = value;
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public Builder clearId() {
+          bitField0_ = (bitField0_ & ~0x00000001);
+          id_ = getDefaultInstance().getId();
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string id = 1;</code>
+         */
+        public Builder setIdBytes(
+            com.google.protobuf.ByteString value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+          id_ = value;
+          onChanged();
+          return this;
+        }
+
+        private java.lang.Object key_ = "";
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public boolean hasKey() {
+          return ((bitField0_ & 0x00000002) == 0x00000002);
+        }
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public java.lang.String getKey() {
+          java.lang.Object ref = key_;
+          if (!(ref instanceof java.lang.String)) {
+            com.google.protobuf.ByteString bs =
+                (com.google.protobuf.ByteString) ref;
+            java.lang.String s = bs.toStringUtf8();
+            if (bs.isValidUtf8()) {
+              key_ = s;
+            }
+            return s;
+          } else {
+            return (java.lang.String) ref;
+          }
+        }
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public com.google.protobuf.ByteString
+            getKeyBytes() {
+          java.lang.Object ref = key_;
+          if (ref instanceof String) {
+            com.google.protobuf.ByteString b = 
+                com.google.protobuf.ByteString.copyFromUtf8(
+                    (java.lang.String) ref);
+            key_ = b;
+            return b;
+          } else {
+            return (com.google.protobuf.ByteString) ref;
+          }
+        }
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public Builder setKey(
+            java.lang.String value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000002;
+          key_ = value;
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public Builder clearKey() {
+          bitField0_ = (bitField0_ & ~0x00000002);
+          key_ = getDefaultInstance().getKey();
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string key = 2;</code>
+         */
+        public Builder setKeyBytes(
+            com.google.protobuf.ByteString value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000002;
+          key_ = value;
+          onChanged();
+          return this;
+        }
+
+        private java.lang.Object qualifier_ = "";
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public boolean hasQualifier() {
+          return ((bitField0_ & 0x00000004) == 0x00000004);
+        }
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public java.lang.String getQualifier() {
+          java.lang.Object ref = qualifier_;
+          if (!(ref instanceof java.lang.String)) {
+            com.google.protobuf.ByteString bs =
+                (com.google.protobuf.ByteString) ref;
+            java.lang.String s = bs.toStringUtf8();
+            if (bs.isValidUtf8()) {
+              qualifier_ = s;
+            }
+            return s;
+          } else {
+            return (java.lang.String) ref;
+          }
+        }
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public com.google.protobuf.ByteString
+            getQualifierBytes() {
+          java.lang.Object ref = qualifier_;
+          if (ref instanceof String) {
+            com.google.protobuf.ByteString b = 
+                com.google.protobuf.ByteString.copyFromUtf8(
+                    (java.lang.String) ref);
+            qualifier_ = b;
+            return b;
+          } else {
+            return (com.google.protobuf.ByteString) ref;
+          }
+        }
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public Builder setQualifier(
+            java.lang.String value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000004;
+          qualifier_ = value;
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public Builder clearQualifier() {
+          bitField0_ = (bitField0_ & ~0x00000004);
+          qualifier_ = getDefaultInstance().getQualifier();
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string qualifier = 3;</code>
+         */
+        public Builder setQualifierBytes(
+            com.google.protobuf.ByteString value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000004;
+          qualifier_ = value;
+          onChanged();
+          return this;
+        }
+
+        private java.lang.Object name_ = "";
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public boolean hasName() {
+          return ((bitField0_ & 0x00000008) == 0x00000008);
+        }
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public java.lang.String getName() {
+          java.lang.Object ref = name_;
+          if (!(ref instanceof java.lang.String)) {
+            com.google.protobuf.ByteString bs =
+                (com.google.protobuf.ByteString) ref;
+            java.lang.String s = bs.toStringUtf8();
+            if (bs.isValidUtf8()) {
+              name_ = s;
+            }
+            return s;
+          } else {
+            return (java.lang.String) ref;
+          }
+        }
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public com.google.protobuf.ByteString
+            getNameBytes() {
+          java.lang.Object ref = name_;
+          if (ref instanceof String) {
+            com.google.protobuf.ByteString b = 
+                com.google.protobuf.ByteString.copyFromUtf8(
+                    (java.lang.String) ref);
+            name_ = b;
+            return b;
+          } else {
+            return (com.google.protobuf.ByteString) ref;
+          }
+        }
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public Builder setName(
+            java.lang.String value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000008;
+          name_ = value;
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public Builder clearName() {
+          bitField0_ = (bitField0_ & ~0x00000008);
+          name_ = getDefaultInstance().getName();
+          onChanged();
+          return this;
+        }
+        /**
+         * <code>optional string name = 4;</code>
+         */
+        public Builder setNameBytes(
+            com.google.protobuf.ByteString value) {
+          if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000008;
+          name_ = value;
+          onChanged();
+          return this;
+        }
+
+        // @@protoc_insertion_point(builder_scope:sonarqube.ws.component.WsSearchResponse.Component)
+      }
+
+      // @@protoc_insertion_point(class_scope:sonarqube.ws.component.WsSearchResponse.Component)
+      private static final org.sonarqube.ws.WsComponents.WsSearchResponse.Component DEFAULT_INSTANCE;
+      static {
+        DEFAULT_INSTANCE = new org.sonarqube.ws.WsComponents.WsSearchResponse.Component();
+      }
+
+      public static org.sonarqube.ws.WsComponents.WsSearchResponse.Component getDefaultInstance() {
+        return DEFAULT_INSTANCE;
+      }
+
+      public static final com.google.protobuf.Parser<Component> PARSER =
+          new com.google.protobuf.AbstractParser<Component>() {
+        public Component parsePartialFrom(
+            com.google.protobuf.CodedInputStream input,
+            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+            throws com.google.protobuf.InvalidProtocolBufferException {
+          try {
+            return new Component(input, extensionRegistry);
+          } catch (RuntimeException e) {
+            if (e.getCause() instanceof
+                com.google.protobuf.InvalidProtocolBufferException) {
+              throw (com.google.protobuf.InvalidProtocolBufferException)
+                  e.getCause();
+            }
+            throw e;
+          }
+        }
+      };
+
+      @java.lang.Override
+      public com.google.protobuf.Parser<Component> getParserForType() {
+        return PARSER;
+      }
+
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.Component getDefaultInstanceForType() {
+        return DEFAULT_INSTANCE;
+      }
+
+    }
+
+    private int bitField0_;
+    public static final int PAGING_FIELD_NUMBER = 1;
+    private org.sonarqube.ws.Common.Paging paging_;
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    public boolean hasPaging() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    public org.sonarqube.ws.Common.Paging getPaging() {
+      return paging_ == null ? org.sonarqube.ws.Common.Paging.getDefaultInstance() : paging_;
+    }
+    /**
+     * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+     */
+    public org.sonarqube.ws.Common.PagingOrBuilder getPagingOrBuilder() {
+      return paging_ == null ? org.sonarqube.ws.Common.Paging.getDefaultInstance() : paging_;
+    }
+
+    public static final int COMPONENTS_FIELD_NUMBER = 2;
+    private java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component> components_;
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    public java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component> getComponentsList() {
+      return components_;
+    }
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    public java.util.List<? extends org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder> 
+        getComponentsOrBuilderList() {
+      return components_;
+    }
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    public int getComponentsCount() {
+      return components_.size();
+    }
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    public org.sonarqube.ws.WsComponents.WsSearchResponse.Component getComponents(int index) {
+      return components_.get(index);
+    }
+    /**
+     * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+     */
+    public org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder getComponentsOrBuilder(
+        int index) {
+      return components_.get(index);
+    }
+
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(1, getPaging());
+      }
+      for (int i = 0; i < components_.size(); i++) {
+        output.writeMessage(2, components_.get(i));
+      }
+      unknownFields.writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, getPaging());
+      }
+      for (int i = 0; i < components_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, components_.get(i));
+      }
+      size += unknownFields.getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(org.sonarqube.ws.WsComponents.WsSearchResponse prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
+    }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code sonarqube.ws.component.WsSearchResponse}
+     *
+     * <pre>
+     * WS api/components/search
+     * </pre>
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:sonarqube.ws.component.WsSearchResponse)
+        org.sonarqube.ws.WsComponents.WsSearchResponseOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.sonarqube.ws.WsComponents.WsSearchResponse.class, org.sonarqube.ws.WsComponents.WsSearchResponse.Builder.class);
+      }
+
+      // Construct using org.sonarqube.ws.WsComponents.WsSearchResponse.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getPagingFieldBuilder();
+          getComponentsFieldBuilder();
+        }
+      }
+      public Builder clear() {
+        super.clear();
+        if (pagingBuilder_ == null) {
+          paging_ = null;
+        } else {
+          pagingBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000001);
+        if (componentsBuilder_ == null) {
+          components_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+        } else {
+          componentsBuilder_.clear();
+        }
+        return this;
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.sonarqube.ws.WsComponents.internal_static_sonarqube_ws_component_WsSearchResponse_descriptor;
+      }
+
+      public org.sonarqube.ws.WsComponents.WsSearchResponse getDefaultInstanceForType() {
+        return org.sonarqube.ws.WsComponents.WsSearchResponse.getDefaultInstance();
+      }
+
+      public org.sonarqube.ws.WsComponents.WsSearchResponse build() {
+        org.sonarqube.ws.WsComponents.WsSearchResponse result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.sonarqube.ws.WsComponents.WsSearchResponse buildPartial() {
+        org.sonarqube.ws.WsComponents.WsSearchResponse result = new org.sonarqube.ws.WsComponents.WsSearchResponse(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        if (pagingBuilder_ == null) {
+          result.paging_ = paging_;
+        } else {
+          result.paging_ = pagingBuilder_.build();
+        }
+        if (componentsBuilder_ == null) {
+          if (((bitField0_ & 0x00000002) == 0x00000002)) {
+            components_ = java.util.Collections.unmodifiableList(components_);
+            bitField0_ = (bitField0_ & ~0x00000002);
+          }
+          result.components_ = components_;
+        } else {
+          result.components_ = componentsBuilder_.build();
+        }
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.sonarqube.ws.WsComponents.WsSearchResponse) {
+          return mergeFrom((org.sonarqube.ws.WsComponents.WsSearchResponse)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.sonarqube.ws.WsComponents.WsSearchResponse other) {
+        if (other == org.sonarqube.ws.WsComponents.WsSearchResponse.getDefaultInstance()) return this;
+        if (other.hasPaging()) {
+          mergePaging(other.getPaging());
+        }
+        if (componentsBuilder_ == null) {
+          if (!other.components_.isEmpty()) {
+            if (components_.isEmpty()) {
+              components_ = other.components_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+            } else {
+              ensureComponentsIsMutable();
+              components_.addAll(other.components_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.components_.isEmpty()) {
+            if (componentsBuilder_.isEmpty()) {
+              componentsBuilder_.dispose();
+              componentsBuilder_ = null;
+              components_ = other.components_;
+              bitField0_ = (bitField0_ & ~0x00000002);
+              componentsBuilder_ = 
+                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+                   getComponentsFieldBuilder() : null;
+            } else {
+              componentsBuilder_.addAllMessages(other.components_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.unknownFields);
+        onChanged();
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.sonarqube.ws.WsComponents.WsSearchResponse parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.sonarqube.ws.WsComponents.WsSearchResponse) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      private org.sonarqube.ws.Common.Paging paging_ = null;
+      private com.google.protobuf.SingleFieldBuilder<
+          org.sonarqube.ws.Common.Paging, org.sonarqube.ws.Common.Paging.Builder, org.sonarqube.ws.Common.PagingOrBuilder> pagingBuilder_;
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public boolean hasPaging() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public org.sonarqube.ws.Common.Paging getPaging() {
+        if (pagingBuilder_ == null) {
+          return paging_ == null ? org.sonarqube.ws.Common.Paging.getDefaultInstance() : paging_;
+        } else {
+          return pagingBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public Builder setPaging(org.sonarqube.ws.Common.Paging value) {
+        if (pagingBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          paging_ = value;
+          onChanged();
+        } else {
+          pagingBuilder_.setMessage(value);
+        }
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public Builder setPaging(
+          org.sonarqube.ws.Common.Paging.Builder builderForValue) {
+        if (pagingBuilder_ == null) {
+          paging_ = builderForValue.build();
+          onChanged();
+        } else {
+          pagingBuilder_.setMessage(builderForValue.build());
+        }
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public Builder mergePaging(org.sonarqube.ws.Common.Paging value) {
+        if (pagingBuilder_ == null) {
+          if (((bitField0_ & 0x00000001) == 0x00000001) &&
+              paging_ != null &&
+              paging_ != org.sonarqube.ws.Common.Paging.getDefaultInstance()) {
+            paging_ =
+              org.sonarqube.ws.Common.Paging.newBuilder(paging_).mergeFrom(value).buildPartial();
+          } else {
+            paging_ = value;
+          }
+          onChanged();
+        } else {
+          pagingBuilder_.mergeFrom(value);
+        }
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public Builder clearPaging() {
+        if (pagingBuilder_ == null) {
+          paging_ = null;
+          onChanged();
+        } else {
+          pagingBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public org.sonarqube.ws.Common.Paging.Builder getPagingBuilder() {
+        bitField0_ |= 0x00000001;
+        onChanged();
+        return getPagingFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      public org.sonarqube.ws.Common.PagingOrBuilder getPagingOrBuilder() {
+        if (pagingBuilder_ != null) {
+          return pagingBuilder_.getMessageOrBuilder();
+        } else {
+          return paging_ == null ?
+              org.sonarqube.ws.Common.Paging.getDefaultInstance() : paging_;
+        }
+      }
+      /**
+       * <code>optional .sonarqube.ws.commons.Paging paging = 1;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilder<
+          org.sonarqube.ws.Common.Paging, org.sonarqube.ws.Common.Paging.Builder, org.sonarqube.ws.Common.PagingOrBuilder> 
+          getPagingFieldBuilder() {
+        if (pagingBuilder_ == null) {
+          pagingBuilder_ = new com.google.protobuf.SingleFieldBuilder<
+              org.sonarqube.ws.Common.Paging, org.sonarqube.ws.Common.Paging.Builder, org.sonarqube.ws.Common.PagingOrBuilder>(
+                  getPaging(),
+                  getParentForChildren(),
+                  isClean());
+          paging_ = null;
+        }
+        return pagingBuilder_;
+      }
+
+      private java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component> components_ =
+        java.util.Collections.emptyList();
+      private void ensureComponentsIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          components_ = new java.util.ArrayList<org.sonarqube.ws.WsComponents.WsSearchResponse.Component>(components_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder, org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder> componentsBuilder_;
+
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component> getComponentsList() {
+        if (componentsBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(components_);
+        } else {
+          return componentsBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public int getComponentsCount() {
+        if (componentsBuilder_ == null) {
+          return components_.size();
+        } else {
+          return componentsBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.Component getComponents(int index) {
+        if (componentsBuilder_ == null) {
+          return components_.get(index);
+        } else {
+          return componentsBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder setComponents(
+          int index, org.sonarqube.ws.WsComponents.WsSearchResponse.Component value) {
+        if (componentsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureComponentsIsMutable();
+          components_.set(index, value);
+          onChanged();
+        } else {
+          componentsBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder setComponents(
+          int index, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder builderForValue) {
+        if (componentsBuilder_ == null) {
+          ensureComponentsIsMutable();
+          components_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          componentsBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder addComponents(org.sonarqube.ws.WsComponents.WsSearchResponse.Component value) {
+        if (componentsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureComponentsIsMutable();
+          components_.add(value);
+          onChanged();
+        } else {
+          componentsBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder addComponents(
+          int index, org.sonarqube.ws.WsComponents.WsSearchResponse.Component value) {
+        if (componentsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureComponentsIsMutable();
+          components_.add(index, value);
+          onChanged();
+        } else {
+          componentsBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder addComponents(
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder builderForValue) {
+        if (componentsBuilder_ == null) {
+          ensureComponentsIsMutable();
+          components_.add(builderForValue.build());
+          onChanged();
+        } else {
+          componentsBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder addComponents(
+          int index, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder builderForValue) {
+        if (componentsBuilder_ == null) {
+          ensureComponentsIsMutable();
+          components_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          componentsBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder addAllComponents(
+          java.lang.Iterable<? extends org.sonarqube.ws.WsComponents.WsSearchResponse.Component> values) {
+        if (componentsBuilder_ == null) {
+          ensureComponentsIsMutable();
+          com.google.protobuf.AbstractMessageLite.Builder.addAll(
+              values, components_);
+          onChanged();
+        } else {
+          componentsBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder clearComponents() {
+        if (componentsBuilder_ == null) {
+          components_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000002);
+          onChanged();
+        } else {
+          componentsBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public Builder removeComponents(int index) {
+        if (componentsBuilder_ == null) {
+          ensureComponentsIsMutable();
+          components_.remove(index);
+          onChanged();
+        } else {
+          componentsBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder getComponentsBuilder(
+          int index) {
+        return getComponentsFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder getComponentsOrBuilder(
+          int index) {
+        if (componentsBuilder_ == null) {
+          return components_.get(index);  } else {
+          return componentsBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public java.util.List<? extends org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder> 
+           getComponentsOrBuilderList() {
+        if (componentsBuilder_ != null) {
+          return componentsBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(components_);
+        }
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder addComponentsBuilder() {
+        return getComponentsFieldBuilder().addBuilder(
+            org.sonarqube.ws.WsComponents.WsSearchResponse.Component.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder addComponentsBuilder(
+          int index) {
+        return getComponentsFieldBuilder().addBuilder(
+            index, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .sonarqube.ws.component.WsSearchResponse.Component components = 2;</code>
+       */
+      public java.util.List<org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder> 
+           getComponentsBuilderList() {
+        return getComponentsFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.sonarqube.ws.WsComponents.WsSearchResponse.Component, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder, org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder> 
+          getComponentsFieldBuilder() {
+        if (componentsBuilder_ == null) {
+          componentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+              org.sonarqube.ws.WsComponents.WsSearchResponse.Component, org.sonarqube.ws.WsComponents.WsSearchResponse.Component.Builder, org.sonarqube.ws.WsComponents.WsSearchResponse.ComponentOrBuilder>(
+                  components_,
+                  ((bitField0_ & 0x00000002) == 0x00000002),
+                  getParentForChildren(),
+                  isClean());
+          components_ = null;
+        }
+        return componentsBuilder_;
+      }
+
+      // @@protoc_insertion_point(builder_scope:sonarqube.ws.component.WsSearchResponse)
+    }
+
+    // @@protoc_insertion_point(class_scope:sonarqube.ws.component.WsSearchResponse)
+    private static final org.sonarqube.ws.WsComponents.WsSearchResponse DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new org.sonarqube.ws.WsComponents.WsSearchResponse();
+    }
+
+    public static org.sonarqube.ws.WsComponents.WsSearchResponse getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    public static final com.google.protobuf.Parser<WsSearchResponse> PARSER =
+        new com.google.protobuf.AbstractParser<WsSearchResponse>() {
+      public WsSearchResponse parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        try {
+          return new WsSearchResponse(input, extensionRegistry);
+        } catch (RuntimeException e) {
+          if (e.getCause() instanceof
+              com.google.protobuf.InvalidProtocolBufferException) {
+            throw (com.google.protobuf.InvalidProtocolBufferException)
+                e.getCause();
+          }
+          throw e;
+        }
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<WsSearchResponse> getParserForType() {
+      return PARSER;
+    }
+
+    public org.sonarqube.ws.WsComponents.WsSearchResponse getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_sonarqube_ws_component_WsSearchResponse_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_sonarqube_ws_component_WsSearchResponse_fieldAccessorTable;
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_sonarqube_ws_component_WsSearchResponse_Component_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\023ws-components.proto\022\026sonarqube.ws.comp" +
+      "onent\032\020ws-commons.proto\"\317\001\n\020WsSearchResp" +
+      "onse\022,\n\006paging\030\001 \001(\0132\034.sonarqube.ws.comm" +
+      "ons.Paging\022F\n\ncomponents\030\002 \003(\01322.sonarqu" +
+      "be.ws.component.WsSearchResponse.Compone" +
+      "nt\032E\n\tComponent\022\n\n\002id\030\001 \001(\t\022\013\n\003key\030\002 \001(\t" +
+      "\022\021\n\tqualifier\030\003 \001(\t\022\014\n\004name\030\004 \001(\tB\"\n\020org" +
+      ".sonarqube.wsB\014WsComponentsH\001"
+    };
+    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+        new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
+          public com.google.protobuf.ExtensionRegistry assignDescriptors(
+              com.google.protobuf.Descriptors.FileDescriptor root) {
+            descriptor = root;
+            return null;
+          }
+        };
+    com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+          org.sonarqube.ws.Common.getDescriptor(),
+        }, assigner);
+    internal_static_sonarqube_ws_component_WsSearchResponse_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_sonarqube_ws_component_WsSearchResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+        internal_static_sonarqube_ws_component_WsSearchResponse_descriptor,
+        new java.lang.String[] { "Paging", "Components", });
+    internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor =
+      internal_static_sonarqube_ws_component_WsSearchResponse_descriptor.getNestedTypes().get(0);
+    internal_static_sonarqube_ws_component_WsSearchResponse_Component_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+        internal_static_sonarqube_ws_component_WsSearchResponse_Component_descriptor,
+        new java.lang.String[] { "Id", "Key", "Qualifier", "Name", });
+    org.sonarqube.ws.Common.getDescriptor();
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto
new file mode 100644 (file)
index 0000000..5b7d68b
--- /dev/null
@@ -0,0 +1,40 @@
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2015 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// SonarQube is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+syntax = "proto2";
+
+package sonarqube.ws.component;
+
+import "ws-commons.proto";
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "WsComponents";
+option optimize_for = SPEED;
+
+// WS api/components/search
+message WsSearchResponse {
+  message Component {
+    optional string id = 1;
+    optional string key = 2;
+    optional string qualifier = 3;
+    optional string name = 4;
+  }
+
+  optional sonarqube.ws.commons.Paging paging = 1;
+  repeated Component components = 2;
+}