@Override
 public ModuleSearchResult completeModules(ModuleQuery query) {
   ModuleSearchResult result = new ModuleSearchResult();
   for (CmrRepository root : getRepositories()) {
     if (query.getNamespace() == null || query.getNamespace().equals(root.getNamespace())) {
       root.completeModules(query, result);
     }
   }
   return result;
 }
 @Override
 public void completeModules(ModuleQuery query, ModuleSearchResult result) {
   // abort if not JVM
   if (query.getType() != ModuleQuery.Type.JVM) return;
   String name = query.getName();
   if (name == null) name = "";
   for (String module : JDK_MODULES) {
     if (module.startsWith(name))
       result.addResult(module, doc(module), null, EmptySet, FixedVersionSet);
   }
 }
 @Override
 public void searchModules(ModuleQuery query, ModuleSearchResult result) {
   // abort if not JVM
   if (query.getType() != ModuleQuery.Type.JVM) return;
   String name = query.getName();
   if (name == null) name = "";
   name = name.toLowerCase();
   boolean stopSearching = false;
   int found = 0;
   for (String module : JDK_MODULES) {
     // does it match?
     if (module.contains(name)) {
       // check if we were already done but were checking for a next results
       if (stopSearching) {
         // we already found enough results but were checking if there
         // were more results to be found for paging, so record that
         // and stop
         result.setHasMoreResults(true);
         return;
       }
       if (query.getStart() == null || found++ >= query.getStart()) {
         // are we interested in this result or did we need to skip it?
         result.addResult(module, doc(module), null, EmptySet, FixedVersionSet);
         // stop if we're done searching
         if (query.getStart() != null
             && query.getCount() != null
             && found >= query.getStart() + query.getCount()) {
           // we're done, but we want to see if there's at least one more result
           // to be found so we can tell clients there's a next page
           stopSearching = true;
         }
       }
     }
   }
 }
  @Override
  public ModuleSearchResult searchModules(ModuleQuery query) {
    if (!query.isPaging()) {
      // that's pretty simple
      ModuleSearchResult result = new ModuleSearchResult();
      for (CmrRepository root : getRepositories()) {
        if (query.getNamespace() == null || query.getNamespace().equals(root.getNamespace())) {
          root.searchModules(query, result);
        }
      }
      return result;
    } else {
      // we need to merge manually
      List<CmrRepository> repos = getRepositories();
      ModuleSearchResult[] results = new ModuleSearchResult[repos.size()];
      // keep an overall module name ordering
      SortedSet<String> names = new TreeSet<>();
      int i = 0;
      long[] pagingInfo = query.getPagingInfo();
      if (pagingInfo != null) {
        // check its length
        if (pagingInfo.length != repos.size())
          throw new IllegalArgumentException(
              "Paging info is not the same size as roots, it must have come from a different RepositoryManager");
      }
      Long start = query.getStart();
      for (CmrRepository root : repos) {
        if (query.getNamespace() == null || query.getNamespace().equals(root.getNamespace())) {
          ModuleSearchResult result = new ModuleSearchResult();
          // adapt the start index if required
          if (pagingInfo != null) query.setStart(pagingInfo[i]);
          root.searchModules(query, result);
          results[i++] = result;
          names.addAll(result.getModuleNames());
        }
      }
      // restore the query start
      query.setStart(start);
      // now merge results
      ModuleSearchResult result = new ModuleSearchResult();
      long[] resultPagingInfo = new long[repos.size()];
      // initialise it if we need to
      if (pagingInfo != null)
        System.arraycopy(pagingInfo, 0, resultPagingInfo, 0, resultPagingInfo.length);

      result.setNextPagingInfo(resultPagingInfo);
      i = 0;
      for (String module : names) {
        // stop if we exceeded the count
        if (query.getCount() != null && i++ == query.getCount()) break;
        // collect every module result for that name from the results
        int repo = 0;
        for (ModuleSearchResult resultPart : results) {
          ModuleDetails details = resultPart.getResult(module);
          // did we find anything in that repo?
          if (details == null) {
            repo++;
            continue;
          } else {
            // count one result for this repo
            resultPagingInfo[repo++]++;
          }
          // merge it
          result.addResult(module, details);
        }
      }
      // see if there are any records left in next pages
      int repo = 0;
      for (ModuleSearchResult resultPart : results) {
        // if we had more results in the first place then we must have another page
        if (resultPart.getHasMoreResults()) {
          result.setHasMoreResults(true);
          break;
        }
        // see how many results we added from this repo
        long resultsAddedForThisRepo;
        if (pagingInfo != null) resultsAddedForThisRepo = resultPagingInfo[repo] - pagingInfo[repo];
        else resultsAddedForThisRepo = resultPagingInfo[repo];
        // did we have more results than we put in?
        if (resultPart.getCount() > resultsAddedForThisRepo) {
          result.setHasMoreResults(true);
          break;
        }
        repo++;
      }
      // record where we started (i is one greater than the number of modules added)
      if (query.getStart() != null) result.setStart(query.getStart());
      else result.setStart(0);
      // all done
      return result;
    }
  }