All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.spark.ui.PagedTable.scala Maven / Gradle / Ivy

There is a newer version: 3.5.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.spark.ui

import java.net.{URLDecoder, URLEncoder}
import java.nio.charset.StandardCharsets.UTF_8
import javax.servlet.http.HttpServletRequest

import scala.collection.JavaConverters._
import scala.xml.{Node, Unparsed}

import com.google.common.base.Splitter

import org.apache.spark.util.Utils

/**
 * A data source that provides data for a page.
 *
 * @param pageSize the number of rows in a page
 */
private[spark] abstract class PagedDataSource[T](val pageSize: Int) {

  /**
   * Return the size of all data.
   */
  protected def dataSize: Int

  /**
   * Slice a range of data.
   */
  protected def sliceData(from: Int, to: Int): Seq[T]

  /**
   * Slice the data for this page
   */
  def pageData(page: Int): PageData[T] = {
    // Display all the data in one page, if the pageSize is less than or equal to zero.
    val pageTableSize = if (pageSize <= 0) {
      dataSize
    } else {
      pageSize
    }
    val totalPages = (dataSize + pageTableSize - 1) / pageTableSize

    val pageToShow = if (page <= 0) {
      1
    } else if (page > totalPages) {
      totalPages
    } else {
      page
    }

    val (from, to) = ((pageToShow - 1) * pageSize, dataSize.min(pageToShow * pageTableSize))

    PageData(totalPages, sliceData(from, to))
  }

}

/**
 * The data returned by `PagedDataSource.pageData`, including the page number, the number of total
 * pages and the data in this page.
 */
private[ui] case class PageData[T](totalPage: Int, data: Seq[T])

/**
 * A paged table that will generate a HTML table for a specified page and also the page navigation.
 */
private[spark] trait PagedTable[T] {

  def tableId: String

  def tableCssClass: String

  def pageSizeFormField: String

  def pageNumberFormField: String

  def dataSource: PagedDataSource[T]

  def headers: Seq[Node]

  def row(t: T): Seq[Node]

  def table(page: Int): Seq[Node] = {
    val _dataSource = dataSource
    try {
      val PageData(totalPages, data) = _dataSource.pageData(page)

      val pageToShow = if (page <= 0) {
        1
      } else if (page > totalPages) {
        totalPages
      } else {
        page
      }
      // Display all the data in one page, if the pageSize is less than or equal to zero.
      val pageSize = if (_dataSource.pageSize <= 0) {
        data.size
      } else {
        _dataSource.pageSize
      }

      val pageNaviTop = pageNavigation(pageToShow, pageSize, totalPages, tableId + "-top")
      val pageNaviBottom = pageNavigation(pageToShow, pageSize, totalPages, tableId + "-bottom")

      
{pageNaviTop} {headers} {data.map(row)}
{pageNaviBottom}
} catch { case e: IndexOutOfBoundsException => val PageData(totalPages, _) = _dataSource.pageData(1)
{pageNavigation(1, _dataSource.pageSize, totalPages)}

Error while rendering table:

              {Utils.exceptionString(e)}
            
} } /** * Return a page navigation. * * It will create a page navigation including a group of page numbers and a form * to submit the page number. * * Here are some examples of the page navigation: * {{{ * << < 11 12 13* 14 15 16 17 18 19 20 > >> * * This is the first group, so "<<" is hidden. * < 1 2* 3 4 5 6 7 8 9 10 > >> * * This is the first group and the first page, so "<<" and "<" are hidden. * 1* 2 3 4 5 6 7 8 9 10 > >> * * Assume totalPages is 19. This is the last group, so ">>" is hidden. * << < 11 12 13* 14 15 16 17 18 19 > * * Assume totalPages is 19. This is the last group and the last page, so ">>" and ">" are hidden. * << < 11 12 13 14 15 16 17 18 19* * * * means the current page number * << means jumping to the first page of the previous group. * < means jumping to the previous page. * >> means jumping to the first page of the next group. * > means jumping to the next page. * }}} */ private[ui] def pageNavigation( page: Int, pageSize: Int, totalPages: Int, navigationId: String = tableId): Seq[Node] = { // A group includes all page numbers will be shown in the page navigation. // The size of group is 10 means there are 10 page numbers will be shown. // The first group is 1 to 10, the second is 2 to 20, and so on val groupSize = 10 val firstGroup = 0 val lastGroup = (totalPages - 1) / groupSize val currentGroup = (page - 1) / groupSize val startPage = currentGroup * groupSize + 1 val endPage = totalPages.min(startPage + groupSize - 1) val pageTags = (startPage to endPage).map { p => if (p == page) { // The current page should be disabled so that it cannot be clicked.
  • {p}
  • } else {
  • {p}
  • } } val hiddenFormFields = { if (goButtonFormPath.contains('?')) { val queryString = goButtonFormPath.split("\\?", 2)(1) val search = queryString.split("#")(0) Splitter .on('&') .trimResults() .omitEmptyStrings() .withKeyValueSeparator("=") .split(search) .asScala .filterKeys(_ != pageSizeFormField) .filterKeys(_ != pageNumberFormField) .mapValues(URLDecoder.decode(_, UTF_8.name())) .map { case (k, v) => } } else { Seq.empty } }
    {hiddenFormFields}
    Page:
      {if (currentGroup > firstGroup) {
    • }} {if (page > 1) {
    • }} {pageTags} {if (page < totalPages) {
    • }} {if (currentGroup < lastGroup) {
    • }}
    } /** * Return a link to jump to a page. */ def pageLink(page: Int): String /** * Returns the submission path for the "go to page #" form. */ def goButtonFormPath: String /** * Returns parameters of other tables in the page. */ def getParameterOtherTable(request: HttpServletRequest, tableTag: String): String = { request.getParameterMap.asScala .filterNot(_._1.startsWith(tableTag)) .map(parameter => parameter._1 + "=" + parameter._2(0)) .mkString("&") } /** * Returns parameter of this table. */ def getTableParameters( request: HttpServletRequest, tableTag: String, defaultSortColumn: String): (String, Boolean, Int) = { val parameterSortColumn = request.getParameter(s"$tableTag.sort") val parameterSortDesc = request.getParameter(s"$tableTag.desc") val parameterPageSize = request.getParameter(s"$tableTag.pageSize") val sortColumn = Option(parameterSortColumn).map { sortColumn => UIUtils.decodeURLParameter(sortColumn) }.getOrElse(defaultSortColumn) val desc = Option(parameterSortDesc).map(_.toBoolean).getOrElse( sortColumn == defaultSortColumn ) val pageSize = Option(parameterPageSize).map(_.toInt).getOrElse(100) (sortColumn, desc, pageSize) } /** * Check if given sort column is valid or not. If invalid then an exception is thrown. */ def isSortColumnValid( headerInfo: Seq[(String, Boolean, Option[String])], sortColumn: String): Unit = { if (!headerInfo.filter(_._2).map(_._1).contains(sortColumn)) { throw new IllegalArgumentException(s"Unknown column: $sortColumn") } } def headerRow( headerInfo: Seq[(String, Boolean, Option[String])], desc: Boolean, pageSize: Int, sortColumn: String, parameterPath: String, tableTag: String, headerId: String): Seq[Node] = { val row: Seq[Node] = { headerInfo.map { case (header, sortable, tooltip) => if (header == sortColumn) { val headerLink = Unparsed( parameterPath + s"&$tableTag.sort=${URLEncoder.encode(header, UTF_8.name())}" + s"&$tableTag.desc=${!desc}" + s"&$tableTag.pageSize=$pageSize" + s"#$headerId") val arrow = if (desc) "▾" else "▴" // UP or DOWN {header} {Unparsed(arrow)} } else { if (sortable) { val headerLink = Unparsed( parameterPath + s"&$tableTag.sort=${URLEncoder.encode(header, UTF_8.name())}" + s"&$tableTag.pageSize=$pageSize" + s"#$headerId") {header} } else { {header} } } } } {row} } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy