protected void deleteSearch(Group group, GoogleSearch search) {
   synchronized (searchLock) {
     googleDB.search.deleteFromGroup(search, group.getId());
     googleDB.rank.deleteBySearch(group.getId(), search.getId());
     if (!googleDB.search.hasGroup(search)) {
       googleDB.serp.deleteBySearch(search.getId());
       googleDB.search.delete(search);
     }
   }
 }
  @FilterWith(XSRFFilter.class)
  public Result generate(
      Context context,
      @Param("groups") Integer groups,
      @Param("searchPerGroup") Integer searchPerGroup,
      @Param("targetPerGroup") Integer targetPerGroup) {
    FlashScope flash = context.getFlashScope();

    if (groups == null
        || groups < 1
        || searchPerGroup == null
        || searchPerGroup < 1
        || targetPerGroup == null
        || targetPerGroup < 1) {
      flash.put("error", "error.invalidParameters");
      return Results.redirect(router.getReverseRoute(DebugController.class, "debug"));
    }

    for (int i = 0; i < groups; i++) {
      Group group = new Group(Group.Module.GOOGLE, "group#" + i);
      baseDB.group.insert(group);

      List<GoogleSearch> searches = new ArrayList<>();
      for (int j = 0; j < searchPerGroup; j++) {
        GoogleSearch search = new GoogleSearch("search#" + j + "#" + group.getName());
        search.setTld("com");
        searches.add(search);
      }
      googleDB.search.insert(searches, group.getId());

      List<GoogleTarget> targets = new ArrayList<>();
      for (int j = 0; j < targetPerGroup; j++) {
        int targetFakeId = j + 1;
        GoogleTarget target =
            new GoogleTarget(
                group.getId(),
                "target#" + targetFakeId + "#" + group.getName(),
                PatternType.REGEX,
                "^https?://www.site" + targetFakeId + ".com.+");
        targets.add(target);
      }
      googleDB.target.insert(targets);
    }

    flash.put("warning", "admin.debug.generated");
    return Results.redirect(router.getReverseRoute(DebugController.class, "debug"));
  }
  @FilterWith({XSRFFilter.class, AdminFilter.class})
  public Result exportSearches(Context context, @Params("id[]") String[] ids) {
    FlashScope flash = context.getFlashScope();
    Group group = context.getAttribute("group", Group.class);

    if (ids == null || ids.length == 0) {
      flash.error("error.noSearchSelected");
      return Results.redirect(
          router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId()));
    }

    List<GoogleSearch> searches = new ArrayList<>();
    for (String id : ids) {
      GoogleSearch search = null;
      try {
        search = getSearch(context, Integer.parseInt(id));
      } catch (Exception ex) {
        search = null;
      }

      if (search == null) {
        flash.error("error.invalidSearch");
        return Results.redirect(
            router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId()));
      }

      searches.add(search);
    }

    StringBuilder builder = new StringBuilder();
    for (GoogleSearch search : searches) {
      builder.append(StringEscapeUtils.escapeCsv(search.getKeyword())).append(",");
      builder.append(search.getTld() != null ? search.getTld() : "com").append(",");
      builder.append(search.getDatacenter() != null ? search.getDatacenter() : "").append(",");
      builder
          .append(
              search.getDevice() != null
                  ? (search.getDevice() == GoogleDevice.DESKTOP ? "desktop" : "mobile")
                  : "")
          .append(",");
      builder
          .append(StringEscapeUtils.escapeCsv(search.getLocal() != null ? search.getLocal() : ""))
          .append(",");
      builder
          .append(
              StringEscapeUtils.escapeCsv(
                  search.getCustomParameters() != null ? search.getCustomParameters() : ""))
          .append("\n");
    }

    return Results.ok().text().render(builder.toString());
  }
  @FilterWith({XSRFFilter.class, AdminFilter.class})
  public Result addSearch(
      Context context,
      @Params("keyword[]") String[] keywords,
      @Params("tld[]") String tlds[],
      @Params("datacenter[]") String[] datacenters,
      @Params("device[]") Integer[] devices,
      @Params("local[]") String[] locals,
      @Params("custom[]") String[] customs) {
    FlashScope flash = context.getFlashScope();
    Group group = context.getAttribute("group", Group.class);

    if (keywords == null
        || tlds == null
        || datacenters == null
        || devices == null
        || locals == null
        || customs == null
        || keywords.length != tlds.length
        || keywords.length != datacenters.length
        || keywords.length != devices.length
        || keywords.length != locals.length
        || keywords.length != customs.length) {
      flash.error("error.invalidParameters");
      return Results.redirect(
          router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId()));
    }

    Set<GoogleSearch> searches = new HashSet<>();

    for (int i = 0; i < keywords.length; i++) {
      GoogleSearch search = new GoogleSearch();

      if (keywords[i].isEmpty()) {
        flash.error("admin.google.keywordEmpty");
        return Results.redirect(
            router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId()));
      }
      search.setKeyword(keywords[i]);

      if (!Validator.isGoogleTLD(tlds[i])) {
        flash.error("admin.google.invalidTLD");
        return Results.redirect(
            router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId()));
      }
      search.setTld(tlds[i]);

      if (!datacenters[i].isEmpty()) {
        if (!Validator.isIPv4(datacenters[i])) {
          flash.error("error.invalidIP");
          return Results.redirect(
              router.getReverseRoute(
                  GoogleGroupController.class, "view", "groupId", group.getId()));
        }
        search.setDatacenter(datacenters[i]);
      }

      if (devices[i] != null && devices[i] >= 0 && devices[i] < GoogleDevice.values().length) {
        search.setDevice(GoogleDevice.values()[devices[i]]);
      } else {
        search.setDevice(GoogleDevice.DESKTOP);
      }

      if (!Validator.isEmpty(locals[i])) {
        search.setLocal(locals[i]);
      }

      if (!Validator.isEmpty(customs[i])) {
        search.setCustomParameters(customs[i]);
      }

      searches.add(search);
    }

    List<GoogleSearch> knownSearches = new ArrayList<>();
    synchronized (searchLock) {
      for (GoogleSearch search : searches) {
        int id = googleDB.search.getId(search);
        if (id > 0) {
          search.setId(id);
          knownSearches.add(search);
        }
      }
      googleDB.search.insert(searches, group.getId());
    }

    googleDB.serpRescan.rescan(null, getTargets(context), knownSearches, false);

    flash.success("google.group.searchInserted");
    return Results.redirect(
        router.getReverseRoute(GoogleGroupController.class, "view", "groupId", group.getId())
            + "#tab-searches");
  }
  public Result jsonSearches(Context context) {
    List<GoogleSearch> searches = context.getAttribute("searches", List.class);
    if (searches.isEmpty()) {
      return Results.json().renderRaw("[]");
    }

    return Results.ok()
        .json()
        .render(
            (Context context0, Result result) -> {
              PrintWriter writer = null;
              OutputStream os = null;
              try {

                String acceptEncoding = context0.getHeader("Accept-Encoding");
                if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
                  result.addHeader("Content-Encoding", "gzip");
                }

                ResponseStreams response = context0.finalizeHeaders(result);
                os = response.getOutputStream();
                if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
                  os = new GZIPOutputStream(os);
                }

                writer = new PrintWriter(os);
                writer.append("[");
                for (int i = 0; i < searches.size(); i++) {
                  GoogleSearch search = searches.get(i);
                  writer.append("{");
                  writer.append("\"id\":").append(Integer.toString(search.getId())).append(",");
                  writer
                      .append("\"keyword\":\"")
                      .append(StringEscapeUtils.escapeJson(search.getKeyword()))
                      .append("\",");
                  writer
                      .append("\"tld\":\"")
                      .append(
                          search.getTld() == null
                              ? ""
                              : StringEscapeUtils.escapeJson(search.getTld()))
                      .append("\",");
                  writer
                      .append("\"device\":\"")
                      .append(SMARTPHONE.equals(search.getDevice()) ? 'M' : 'D')
                      .append("\",");
                  writer
                      .append("\"local\":\"")
                      .append(
                          search.getLocal() == null
                              ? ""
                              : StringEscapeUtils.escapeJson(search.getLocal()))
                      .append("\",");
                  writer
                      .append("\"datacenter\":\"")
                      .append(
                          search.getDatacenter() == null
                              ? ""
                              : StringEscapeUtils.escapeJson(search.getDatacenter()))
                      .append("\",");
                  writer
                      .append("\"custom\":\"")
                      .append(
                          search.getCustomParameters() == null
                              ? ""
                              : StringEscapeUtils.escapeJson(search.getCustomParameters()))
                      .append("\"");
                  writer.append("}");
                  if (i != searches.size() - 1) {
                    writer.append(",");
                  }
                }
                writer.append("]");

              } catch (Exception ex) {
                LOG.warn("HTTP error", ex);
              } finally {
                if (os != null) {
                  try {
                    writer.close();
                    os.close();
                  } catch (Exception ex) {
                  }
                }
              }
            });
  }