/**
   * The URL query string of of the next page.
   *
   * <p>Note: the query string does not start with a "?" or "&" those need to be added as
   * appropriate by the caller.
   */
  private String getNextPageURL(BrowseParams params, BrowseInfo info)
      throws SQLException, UIException {
    // Don't create a next page link if this is the last page
    if (info.isLast()) return null;

    Map<String, String> parameters = new HashMap<String, String>();
    parameters.putAll(params.getCommonParameters());
    parameters.putAll(params.getControlParameters());

    if (info.hasNextPage()) {
      if (info.getNextItem() < 0)
        parameters.put(BrowseParams.JUMPTO_VALUE, URLEncode(info.getNextValue()));
      else parameters.put(BrowseParams.JUMPTO_ITEM, Integer.toString(info.getNextItem()));
    }

    return super.generateURL(BROWSE_URL_BASE, parameters);
  }
  public Serializable getKey() {
    try {
      BrowseParams params = getUserParams();

      String key = params.getKey();

      if (key != null) {
        DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
        if (dso != null) key += "-" + dso.getHandle();

        return HashUtil.hash(key);
      }
    } catch (Exception e) {
      // Ignore all errors and just don't cache.
    }

    return "0";
  }
  /**
   * Get the query parameters supplied to the browse.
   *
   * @return
   * @throws SQLException
   * @throws UIException
   */
  private BrowseParams getUserParams() throws SQLException, UIException {
    if (this.userParams != null) return this.userParams;

    Context context = ContextUtil.obtainContext(objectModel);
    Request request = ObjectModelHelper.getRequest(objectModel);

    BrowseParams params = new BrowseParams();

    params.month = request.getParameter(BrowseParams.MONTH);
    params.year = request.getParameter(BrowseParams.YEAR);
    params.etAl = RequestUtils.getIntParameter(request, BrowseParams.ETAL);

    params.scope = new BrowserScope(context);

    // Are we in a community or collection?
    DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
    if (dso instanceof Community) params.scope.setCommunity((Community) dso);
    if (dso instanceof Collection) params.scope.setCollection((Collection) dso);

    try {
      String type = request.getParameter(BrowseParams.TYPE);
      int sortBy = RequestUtils.getIntParameter(request, BrowseParams.SORT_BY);

      BrowseIndex bi = BrowseIndex.getBrowseIndex(type);
      if (bi == null) {
        throw new BrowseException("There is no browse index of the type: " + type);
      }

      // If we don't have a sort column
      if (sortBy == -1) {
        // Get the default one
        SortOption so = bi.getSortOption();
        if (so != null) {
          sortBy = so.getNumber();
        }
      } else if (bi.isItemIndex() && !bi.isInternalIndex()) {
        // If a default sort option is specified by the index, but it isn't
        // the same as sort option requested, attempt to find an index that
        // is configured to use that sort by default
        // This is so that we can then highlight the correct option in the navigation
        SortOption bso = bi.getSortOption();
        SortOption so = SortOption.getSortOption(sortBy);
        if (bso != null && bso != so) {
          BrowseIndex newBi = BrowseIndex.getBrowseIndex(so);
          if (newBi != null) {
            bi = newBi;
            type = bi.getName();
          }
        }
      }

      params.scope.setBrowseIndex(bi);
      params.scope.setSortBy(sortBy);

      params.scope.setJumpToItem(RequestUtils.getIntParameter(request, BrowseParams.JUMPTO_ITEM));
      params.scope.setOrder(request.getParameter(BrowseParams.ORDER));
      params.scope.setResultsPerPage(
          RequestUtils.getIntParameter(request, BrowseParams.RESULTS_PER_PAGE));
      params.scope.setStartsWith(request.getParameter(BrowseParams.STARTS_WITH));
      params.scope.setFilterValue(request.getParameter(BrowseParams.FILTER_VALUE));
      params.scope.setJumpToValue(request.getParameter(BrowseParams.JUMPTO_VALUE));
      params.scope.setJumpToValueLang(request.getParameter(BrowseParams.JUMPTO_VALUE_LANG));
      params.scope.setFilterValueLang(request.getParameter(BrowseParams.FILTER_VALUE_LANG));

      // Filtering to a value implies this is a second level browse
      if (params.scope.getFilterValue() != null) params.scope.setBrowseLevel(1);

      // if year and perhaps month have been selected, we translate these
      // into "startsWith"
      // if startsWith has already been defined then it is overwritten
      if (params.year != null && !"".equals(params.year) && !"-1".equals(params.year)) {
        String startsWith = params.year;
        if ((params.month != null) && !"-1".equals(params.month) && !"".equals(params.month)) {
          // subtract 1 from the month, so the match works
          // appropriately
          if ("ASC".equals(params.scope.getOrder())) {
            params.month = Integer.toString((Integer.parseInt(params.month) - 1));
          }

          // They've selected a month as well
          if (params.month.length() == 1) {
            // Ensure double-digit month number
            params.month = "0" + params.month;
          }

          startsWith = params.year + "-" + params.month;
        }

        params.scope.setStartsWith(startsWith);
      }
    } catch (BrowseException bex) {
      throw new UIException("Unable to create browse parameters", bex);
    }

    this.userParams = params;
    return params;
  }
  /**
   * Add the controls to changing sorting and display options.
   *
   * @param div
   * @param info
   * @param params
   * @throws WingException
   */
  private void addBrowseControls(Division div, BrowseInfo info, BrowseParams params)
      throws WingException {
    // Prepare a Map of query parameters required for all links
    Map<String, String> queryParams = new HashMap<String, String>();

    queryParams.putAll(params.getCommonParameters());

    Division controls =
        div.addInteractiveDivision(
            "browse-controls", BROWSE_URL_BASE, Division.METHOD_POST, "browse controls");

    // Add all the query parameters as hidden fields on the form
    for (String key : queryParams.keySet()) controls.addHidden(key).setValue(queryParams.get(key));

    Para controlsForm = controls.addPara();

    // If we are browsing a list of items
    if (isItemBrowse(info)) //  && info.isSecondLevel()
    {
      try {
        // Create a drop down of the different sort columns available
        Set<SortOption> sortOptions = SortOption.getSortOptions();

        // Only generate the list if we have multiple columns
        if (sortOptions.size() > 1) {
          controlsForm.addContent(T_sort_by);
          Select sortSelect = controlsForm.addSelect(BrowseParams.SORT_BY);

          for (SortOption so : sortOptions) {
            sortSelect.addOption(
                so.equals(info.getSortOption()),
                so.getNumber(),
                message("xmlui.ArtifactBrowser.ConfigurableBrowse.sort_by." + so.getName()));
          }
        }
      } catch (BrowseException be) {
        throw new WingException("Unable to get sort options", be);
      }
    }

    // Create a control to changing ascending / descending order
    controlsForm.addContent(T_order);
    Select orderSelect = controlsForm.addSelect(BrowseParams.ORDER);
    orderSelect.addOption("ASC".equals(params.scope.getOrder()), "ASC", T_order_asc);
    orderSelect.addOption("DESC".equals(params.scope.getOrder()), "DESC", T_order_desc);

    // Create a control for the number of records to display
    controlsForm.addContent(T_rpp);
    Select rppSelect = controlsForm.addSelect(BrowseParams.RESULTS_PER_PAGE);
    for (int i = 5; i <= 100; i += 5) {
      rppSelect.addOption((i == info.getResultsPerPage()), i, Integer.toString(i));
    }

    // Create a control for the number of authors per item to display
    // FIXME This is currently disabled, as the supporting functionality
    // is not currently present in xmlui
    // if (isItemBrowse(info))
    // {
    //    controlsForm.addContent(T_etal);
    //    Select etalSelect = controlsForm.addSelect(BrowseParams.ETAL);
    //
    //    etalSelect.addOption((info.getEtAl() < 0), 0, T_etal_all);
    //    etalSelect.addOption(1 == info.getEtAl(), 1, Integer.toString(1));
    //
    //    for (int i = 5; i <= 50; i += 5)
    //    {
    //        etalSelect.addOption(i == info.getEtAl(), i, Integer.toString(i));
    //    }
    // }

    controlsForm.addButton("update").setValue(T_update);
  }
  /**
   * Makes the jump-list navigation for the results
   *
   * @param div
   * @param info
   * @param params
   * @throws WingException
   */
  private void addBrowseJumpNavigation(Division div, BrowseInfo info, BrowseParams params)
      throws WingException {
    // Get the name of the index
    String type = info.getBrowseIndex().getName();

    // Prepare a Map of query parameters required for all links
    Map<String, String> queryParams = new HashMap<String, String>();
    queryParams.putAll(params.getCommonParameters());
    queryParams.putAll(params.getControlParameters());

    // Navigation aid (really this is a poor version of pagination)
    Division jump =
        div.addInteractiveDivision(
            "browse-navigation", BROWSE_URL_BASE, Division.METHOD_POST, "secondary navigation");

    // Add all the query parameters as hidden fields on the form
    for (String key : queryParams.keySet()) jump.addHidden(key).setValue(queryParams.get(key));

    // If this is a date based browse, render the date navigation
    if (isSortedByDate(info)) {
      Para jumpForm = jump.addPara();

      // Create a select list to choose a month
      jumpForm.addContent(T_jump_select);
      Select month = jumpForm.addSelect(BrowseParams.MONTH);
      month.addOption(false, "-1", T_choose_month);
      for (int i = 1; i <= 12; i++) {
        month.addOption(false, String.valueOf(i), DCDate.getMonthName(i, Locale.getDefault()));
      }

      // Create a select list to choose a year
      Select year = jumpForm.addSelect(BrowseParams.YEAR);
      year.addOption(false, "-1", T_choose_year);
      int currentYear = DCDate.getCurrent().getYear();
      int i = currentYear;

      // Calculate where to move from 1, 5 to 10 year jumps
      int oneYearBreak = ((currentYear - ONE_YEAR_LIMIT) / 5) * 5;
      int fiveYearBreak = ((currentYear - FIVE_YEAR_LIMIT) / 10) * 10;
      int tenYearBreak = (currentYear - TEN_YEAR_LIMIT);
      do {
        year.addOption(false, String.valueOf(i), String.valueOf(i));

        if (i <= fiveYearBreak) i -= 10;
        else if (i <= oneYearBreak) i -= 5;
        else i--;
      } while (i > tenYearBreak);

      // Create a free text entry box for the year
      jumpForm = jump.addPara();
      jumpForm.addContent(T_jump_year);
      jumpForm.addText("start_with").setHelp(T_jump_year_help);

      jumpForm.addButton("submit").setValue(T_go);
    } else {
      // Create a clickable list of the alphabet
      List jumpList = jump.addList("jump-list", List.TYPE_SIMPLE, "alphabet");
      for (char c = 'A'; c <= 'Z'; c++) {
        Map<String, String> cQuery = new HashMap<String, String>(queryParams);
        cQuery.put(BrowseParams.STARTS_WITH, Character.toString(c));
        jumpList.addItemXref(super.generateURL(BROWSE_URL_BASE, cQuery), Character.toString(c));
      }

      // Create a free text field for the initial characters
      Para jumpForm = jump.addPara();
      jumpForm.addContent(T_starts_with);
      jumpForm.addText(BrowseParams.STARTS_WITH).setHelp(T_starts_with_help);

      jumpForm.addButton("submit").setValue(T_go);
    }
  }