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

org.htmlunit.html.HtmlTable Maven / Gradle / Ivy

Go to download

XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.

There is a newer version: 8.4.1
Show newest version
/*
 * Copyright (c) 2002-2024 Gargoyle Software Inc.
 *
 * Licensed 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
 * https://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.htmlunit.html;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import org.htmlunit.ElementNotFoundException;
import org.htmlunit.SgmlPage;

/**
 * Wrapper for the HTML element "table".
 *
 * @author Mike Bowler
 * @author David K. Taylor
 * @author Christian Sell
 * @author Ahmed Ashour
 * @author Ronald Brill
 * @author Frank Danek
 */
public class HtmlTable extends HtmlElement {

    /** The HTML tag represented by this element. */
    public static final String TAG_NAME = "table";

    /**
     * Creates an instance.
     *
     * @param qualifiedName the qualified name of the element type to instantiate
     * @param page the page that contains this element
     * @param attributes the initial attributes
     */
    HtmlTable(final String qualifiedName, final SgmlPage page,
            final Map attributes) {
        super(qualifiedName, page, attributes);
    }

    /**
     * Returns the first cell that matches the specified row and column, searching left to right, top to bottom.
     * 

This method returns different values than getRow(rowIndex).getCell(cellIndex) because this takes cellspan * and rowspan into account.
* This means, a cell with colspan='2' consumes two columns; a cell with rowspan='3' consumes three rows. The * index is based on the 'background' model of the table; if you have a row like
* <td>cell1</td> <td colspan='2'>cell2</td> then this row is treated as a row with * three cells.
*

* * getCellAt(rowIndex, 0).asText() returns "cell1";
* getCellAt(rowIndex, 1).asText() returns "cell2";
* getCellAt(rowIndex, 2).asText() returns "cell2"; and
* getCellAt(rowIndex, 3).asText() returns null; *
*

* * @param rowIndex the row index * @param columnIndex the column index * @return the HtmlTableCell at that location or null if there are no cells at that location */ public final HtmlTableCell getCellAt(final int rowIndex, final int columnIndex) { final RowIterator rowIterator = getRowIterator(); final HashSet occupied = new HashSet<>(); int row = 0; for (final HtmlTableRow htmlTableRow : rowIterator) { final HtmlTableRow.CellIterator cellIterator = htmlTableRow.getCellIterator(); int col = 0; for (final HtmlTableCell cell : cellIterator) { while (occupied.contains(new Position(row, col))) { col++; } final int nextRow = row + cell.getRowSpan(); if (row <= rowIndex && nextRow > rowIndex) { final int nextCol = col + cell.getColumnSpan(); if (col <= columnIndex && nextCol > columnIndex) { return cell; } } if (cell.getRowSpan() > 1 || cell.getColumnSpan() > 1) { for (int i = 0; i < cell.getRowSpan(); i++) { for (int j = 0; j < cell.getColumnSpan(); j++) { occupied.add(new Position(row + i, col + j)); } } } col++; } row++; } return null; } /** * @return an iterator over all the HtmlTableRow objects */ private RowIterator getRowIterator() { return new RowIterator(); } /** * @return an immutable list containing all the HtmlTableRow objects * @see #getRowIterator */ public List getRows() { final List result = new ArrayList<>(); for (final HtmlTableRow row : getRowIterator()) { result.add(row); } return Collections.unmodifiableList(result); } /** * @param index the 0-based index of the row * @return the HtmlTableRow at the given index * @throws IndexOutOfBoundsException if there is no row at the given index * @see #getRowIterator */ public HtmlTableRow getRow(final int index) throws IndexOutOfBoundsException { int count = 0; for (final HtmlTableRow row : getRowIterator()) { if (count == index) { return row; } count++; } throw new IndexOutOfBoundsException("No row found for index " + index + "."); } /** * Computes the number of rows in this table. Note that the count is computed dynamically * by iterating over all rows. * * @return the number of rows in this table */ public final int getRowCount() { int count = 0; for (final RowIterator iterator = getRowIterator(); iterator.hasNext(); iterator.next()) { count++; } return count; } /** * Finds and return the row with the specified id. * * @param id the id of the row * @return the row with the specified id * @exception ElementNotFoundException If the row cannot be found. */ public final HtmlTableRow getRowById(final String id) throws ElementNotFoundException { for (final HtmlTableRow row : getRowIterator()) { if (row.getId().equals(id)) { return row; } } throw new ElementNotFoundException("tr", DomElement.ID_ATTRIBUTE, id); } /** * Returns the table caption text or an empty string if a caption wasn't specified. * * @return the caption text */ public String getCaptionText() { for (final DomElement element : getChildElements()) { if (element instanceof HtmlCaption) { return element.asNormalizedText(); } } return null; } /** * Returns the table header or null if a header wasn't specified. * * @return the table header */ public HtmlTableHeader getHeader() { for (final DomElement element : getChildElements()) { if (element instanceof HtmlTableHeader) { return (HtmlTableHeader) element; } } return null; } /** * Returns the table footer or null if a footer wasn't specified. * * @return the table footer */ public HtmlTableFooter getFooter() { for (final DomElement element : getChildElements()) { if (element instanceof HtmlTableFooter) { return (HtmlTableFooter) element; } } return null; } /** * Returns a list of tables bodies defined in this table. If no bodies were defined * then an empty list will be returned. * * @return a list of {@link HtmlTableBody} objects */ public List getBodies() { final List bodies = new ArrayList<>(); for (final DomElement element : getChildElements()) { if (element instanceof HtmlTableBody) { bodies.add((HtmlTableBody) element); } } return bodies; } /** * Returns the value of the attribute {@code summary}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code summary} * or an empty string if that attribute isn't defined. */ public final String getSummaryAttribute() { return getAttributeDirect("summary"); } /** * Returns the value of the attribute {@code width}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code width} * or an empty string if that attribute isn't defined. */ public final String getWidthAttribute() { return getAttributeDirect("width"); } /** * Returns the value of the attribute {@code border}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code border} * or an empty string if that attribute isn't defined. */ public final String getBorderAttribute() { return getAttributeDirect("border"); } /** * Returns the value of the attribute {@code frame}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code frame} * or an empty string if that attribute isn't defined. */ public final String getFrameAttribute() { return getAttributeDirect("frame"); } /** * Returns the value of the attribute {@code rules}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code rules} * or an empty string if that attribute isn't defined. */ public final String getRulesAttribute() { return getAttributeDirect("rules"); } /** * Returns the value of the attribute {@code cellspacing}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code cellspacing} * or an empty string if that attribute isn't defined. */ public final String getCellSpacingAttribute() { return getAttributeDirect("cellspacing"); } /** * Returns the value of the attribute {@code cellpadding}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code cellpadding} * or an empty string if that attribute isn't defined. */ public final String getCellPaddingAttribute() { return getAttributeDirect("cellpadding"); } /** * Returns the value of the attribute {@code align}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code align} * or an empty string if that attribute isn't defined. */ public final String getAlignAttribute() { return getAttributeDirect("align"); } /** * Returns the value of the attribute {@code bgcolor}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code bgcolor} * or an empty string if that attribute isn't defined. */ public final String getBgcolorAttribute() { return getAttributeDirect("bgcolor"); } /** * An iterator that moves over all rows in this table. The iterator will also * enter into nested row group elements (header, footer and body). */ private class RowIterator implements Iterator, Iterable { private HtmlTableRow nextRow_; private TableRowGroup currentGroup_; /** Creates a new instance. */ RowIterator() { setNextRow(getFirstChild()); } /** * @return {@code true} if there are more rows available */ @Override public boolean hasNext() { return nextRow_ != null; } /** * @return the next row from this iterator * @throws NoSuchElementException if no more rows are available */ @Override public HtmlTableRow next() throws NoSuchElementException { return nextRow(); } /** * Removes the current row from the underlying table. */ @Override public void remove() { if (nextRow_ == null) { throw new IllegalStateException(); } final DomNode sibling = nextRow_.getPreviousSibling(); if (sibling != null) { sibling.remove(); } } /** * @return the next row from this iterator * @throws NoSuchElementException if no more rows are available */ public HtmlTableRow nextRow() throws NoSuchElementException { if (nextRow_ != null) { final HtmlTableRow result = nextRow_; setNextRow(nextRow_.getNextSibling()); return result; } throw new NoSuchElementException(); } /** * Sets the internal position to the next row, starting at the given node. * @param node the node to mark as the next row; if this is not a row, the * next reachable row will be marked. */ private void setNextRow(final DomNode node) { nextRow_ = null; for (DomNode next = node; next != null; next = next.getNextSibling()) { if (next instanceof HtmlTableRow) { nextRow_ = (HtmlTableRow) next; return; } else if (currentGroup_ == null && next instanceof TableRowGroup) { currentGroup_ = (TableRowGroup) next; setNextRow(next.getFirstChild()); return; } } if (currentGroup_ != null) { final DomNode group = currentGroup_; currentGroup_ = null; setNextRow(group.getNextSibling()); } } @Override public Iterator iterator() { return this; } } /** * {@inheritDoc} * @return {@code true} as browsers ignore self closing table tags. */ @Override protected boolean isEmptyXmlTagExpanded() { return true; } /** * {@inheritDoc} */ @Override public DisplayStyle getDefaultStyleDisplay() { return DisplayStyle.TABLE; } private static final class Position { private final int posX_; private final int posY_; private Position(final int x, final int y) { posX_ = x; posY_ = y; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + posX_; result = prime * result + posY_; return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Position other = (Position) obj; if (posX_ != other.posX_) { return false; } if (posY_ != other.posY_) { return false; } return true; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy