private List<PagingLink> getPagingLinks(
      int startIndex,
      int hitsPerPage,
      int hitsLength,
      int maxHitSize,
      String baseUrl,
      ParamMap params) {

    List<PagingLink> pagingLinks = new ArrayList<PagingLink>();

    // No paging links if only one page of results
    if (hitsLength <= hitsPerPage) {
      return pagingLinks;
    }

    for (int i = 0; i < hitsLength; i += hitsPerPage) {
      params.put("startIndex", String.valueOf(i));
      if (i < maxHitSize - hitsPerPage) {
        int pageNumber = i / hitsPerPage + 1;
        if (i >= startIndex && i < (startIndex + hitsPerPage)) {
          pagingLinks.add(new PagingLink(pageNumber));
        } else {
          pagingLinks.add(new PagingLink(pageNumber, baseUrl, params));
        }
      } else {
        pagingLinks.add(new PagingLink("more...", baseUrl, params));
      }
    }

    return pagingLinks;
  }
 private String getNextPageLink(int startIndex, int hitsPerPage, String baseUrl, ParamMap params) {
   params.put("startIndex", String.valueOf(startIndex + hitsPerPage));
   // return new PagingLink("Next", baseUrl, params);
   return UrlBuilder.getUrl(baseUrl, params);
 }
  @Override
  protected ResponseValues processRequest(VitroRequest vreq) {
    // There may be other non-html formats in the future
    Format format = getFormat(vreq);
    boolean wasXmlRequested = Format.XML == format;
    log.debug("xml was the requested format");
    boolean wasHtmlRequested = !wasXmlRequested;

    try {
      Portal portal = vreq.getPortal();
      PortalFlag portalFlag = vreq.getPortalFlag();

      // make sure an IndividualDao is available
      if (vreq.getWebappDaoFactory() == null
          || vreq.getWebappDaoFactory().getIndividualDao() == null) {
        log.error("Could not get webappDaoFactory or IndividualDao");
        throw new Exception("Could not access model.");
      }
      IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
      VClassGroupDao grpDao = vreq.getWebappDaoFactory().getVClassGroupDao();
      VClassDao vclassDao = vreq.getWebappDaoFactory().getVClassDao();
      String alphaFilter = vreq.getParameter("alpha");

      int startIndex = 0;
      try {
        startIndex = Integer.parseInt(vreq.getParameter("startIndex"));
      } catch (Throwable e) {
        startIndex = 0;
      }
      log.debug("startIndex is " + startIndex);

      int hitsPerPage = defaultHitsPerPage;
      try {
        hitsPerPage = Integer.parseInt(vreq.getParameter("hitsPerPage"));
      } catch (Throwable e) {
        hitsPerPage = defaultHitsPerPage;
      }
      log.debug("hitsPerPage is " + hitsPerPage);

      int maxHitSize = defaultMaxSearchSize;
      if (startIndex >= defaultMaxSearchSize - hitsPerPage)
        maxHitSize = startIndex + defaultMaxSearchSize;
      if (alphaFilter != null) {
        maxHitSize = maxHitSize * 2;
        hitsPerPage = maxHitSize;
      }
      log.debug("maxHitSize is " + maxHitSize);

      String qtxt = vreq.getParameter(VitroQuery.QUERY_PARAMETER_NAME);
      Analyzer analyzer = getAnalyzer(getServletContext());

      Query query = null;
      try {
        query = getQuery(vreq, portalFlag, analyzer, qtxt);
        log.debug("query for '" + qtxt + "' is " + query.toString());
      } catch (ParseException e) {
        return doBadQuery(portal, qtxt, format);
      }

      IndexSearcher searcherForRequest = LuceneIndexFactory.getIndexSearcher(getServletContext());

      TopDocs topDocs = null;
      try {
        topDocs = searcherForRequest.search(query, null, maxHitSize);
      } catch (Throwable t) {
        log.error("in first pass at search: " + t);
        // this is a hack to deal with odd cases where search and index threads interact
        try {
          wait(150);
          topDocs = searcherForRequest.search(query, null, maxHitSize);
        } catch (Exception ex) {
          log.error(ex);
          String msg = makeBadSearchMessage(qtxt, ex.getMessage());
          if (msg == null) {
            msg = "The search request contained errors.";
          }
          return doFailedSearch(msg, qtxt, format);
        }
      }

      if (topDocs == null || topDocs.scoreDocs == null) {
        log.error("topDocs for a search was null");
        String msg = "The search request contained errors.";
        return doFailedSearch(msg, qtxt, format);
      }

      int hitsLength = topDocs.scoreDocs.length;
      if (hitsLength < 1) {
        return doNoHits(qtxt, format);
      }
      log.debug("found " + hitsLength + " hits");

      int lastHitToShow = 0;
      if ((startIndex + hitsPerPage) > hitsLength) {
        lastHitToShow = hitsLength;
      } else {
        lastHitToShow = startIndex + hitsPerPage - 1;
      }

      List<Individual> beans = new LinkedList<Individual>();
      for (int i = startIndex; i < topDocs.scoreDocs.length; i++) {
        try {
          if ((i >= startIndex) && (i <= lastHitToShow)) {
            Document doc = searcherForRequest.doc(topDocs.scoreDocs[i].doc);
            String uri = doc.get(Entity2LuceneDoc.term.URI);
            Individual ent = new IndividualImpl();
            ent.setURI(uri);
            ent = iDao.getIndividualByURI(uri);
            if (ent != null) beans.add(ent);
          }
        } catch (Exception e) {
          log.error("problem getting usable Individuals from search " + "hits" + e.getMessage());
        }
      }

      ParamMap pagingLinkParams = new ParamMap();
      pagingLinkParams.put("querytext", qtxt);
      pagingLinkParams.put("hitsPerPage", String.valueOf(hitsPerPage));

      if (wasXmlRequested) {
        pagingLinkParams.put(XML_REQUEST_PARAM, "1");
      }

      /* Start putting together the data for the templates */

      Map<String, Object> body = new HashMap<String, Object>();

      String classGroupParam = vreq.getParameter("classgroup");
      boolean classGroupFilterRequested = false;
      if (!StringUtils.isEmpty(classGroupParam)) {
        VClassGroup grp = grpDao.getGroupByURI(classGroupParam);
        classGroupFilterRequested = true;
        if (grp != null && grp.getPublicName() != null)
          body.put("classGroupName", grp.getPublicName());
      }

      String typeParam = vreq.getParameter("type");
      boolean typeFiltereRequested = false;
      if (!StringUtils.isEmpty(typeParam)) {
        VClass type = vclassDao.getVClassByURI(typeParam);
        typeFiltereRequested = true;
        if (type != null && type.getName() != null) body.put("typeName", type.getName());
      }

      /* Add classgroup and type refinement links to body */
      if (wasHtmlRequested) {
        // Search request includes no classgroup and no type, so add classgroup search refinement
        // links.
        if (!classGroupFilterRequested && !typeFiltereRequested) {
          List<VClassGroup> classgroups = getClassGroups(grpDao, topDocs, searcherForRequest);
          List<VClassGroupSearchLink> classGroupLinks =
              new ArrayList<VClassGroupSearchLink>(classgroups.size());
          for (VClassGroup vcg : classgroups) {
            if (vcg.getPublicName() != null) {
              classGroupLinks.add(new VClassGroupSearchLink(qtxt, vcg));
            }
          }
          body.put("classGroupLinks", classGroupLinks);

          // Search request is for a classgroup, so add rdf:type search refinement links
          // but try to filter out classes that are subclasses
        } else if (classGroupFilterRequested && !typeFiltereRequested) {
          List<VClass> vClasses = getVClasses(vclassDao, topDocs, searcherForRequest);
          List<VClassSearchLink> vClassLinks = new ArrayList<VClassSearchLink>(vClasses.size());
          for (VClass vc : vClasses) {
            vClassLinks.add(new VClassSearchLink(qtxt, vc));
          }
          body.put("classLinks", vClassLinks);
          pagingLinkParams.put("classgroup", classGroupParam);

          // This case is never displayed
        } else if (!StringUtils.isEmpty(alphaFilter)) {
          body.put("alphas", getAlphas(topDocs, searcherForRequest));
          alphaSortIndividuals(beans);
        } else {
          pagingLinkParams.put("type", typeParam);
        }
      }

      // Convert search result individuals to template model objects
      body.put(
          "individuals", ListedIndividualTemplateModel.getIndividualTemplateModelList(beans, vreq));

      body.put("querytext", qtxt);
      body.put("title", qtxt + " - " + portal.getAppName() + " Search Results");

      body.put("hitsLength", hitsLength);
      body.put("startIndex", startIndex);

      body.put(
          "pagingLinks",
          getPagingLinks(
              startIndex,
              hitsPerPage,
              hitsLength,
              maxHitSize,
              vreq.getServletPath(),
              pagingLinkParams));

      if (startIndex != 0) {
        body.put(
            "prevPage",
            getPreviousPageLink(startIndex, hitsPerPage, vreq.getServletPath(), pagingLinkParams));
      }
      if (startIndex < (hitsLength - hitsPerPage)) {
        body.put(
            "nextPage",
            getNextPageLink(startIndex, hitsPerPage, vreq.getServletPath(), pagingLinkParams));
      }

      String template = templateTable.get(format).get(Result.PAGED);

      return new TemplateResponseValues(template, body);
    } catch (Throwable e) {
      return doSearchError(e, format);
    }
  }