View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.html;
16  
17  import java.io.Serializable;
18  import java.util.function.Predicate;
19  
20  import org.htmlunit.html.DomElement;
21  import org.htmlunit.html.DomNode;
22  import org.htmlunit.html.HtmlPage;
23  import org.htmlunit.html.HtmlTableRow;
24  import org.htmlunit.javascript.HtmlUnitScriptable;
25  import org.htmlunit.javascript.JavaScriptEngine;
26  import org.htmlunit.javascript.configuration.JsxClass;
27  import org.htmlunit.javascript.configuration.JsxFunction;
28  import org.htmlunit.javascript.configuration.JsxGetter;
29  import org.htmlunit.javascript.configuration.JsxSetter;
30  import org.htmlunit.javascript.host.dom.DOMException;
31  
32  /**
33   * Superclass for all row-containing JavaScript host classes, including tables,
34   * table headers, table bodies and table footers.
35   *
36   * @author Daniel Gredler
37   * @author Chris Erskine
38   * @author Marc Guillemot
39   * @author Ahmed Ashour
40   */
41  @JsxClass(isJSObject = false)
42  public class RowContainer extends HTMLElement {
43  
44      /**
45       * Returns the rows in the element.
46       * @return the rows in the element
47       */
48      @JsxGetter
49      public HTMLCollection getRows() {
50          final HTMLCollection rows = new HTMLCollection(getDomNodeOrDie(), false);
51          rows.setIsMatchingPredicate(
52                  (Predicate<DomNode> & Serializable)
53                  node -> node instanceof HtmlTableRow && isContainedRow((HtmlTableRow) node));
54          return rows;
55      }
56  
57      /**
58       * Indicates if the row belongs to this container.
59       * @param row the row to test
60       * @return {@code true} if it belongs to this container
61       */
62      protected boolean isContainedRow(final HtmlTableRow row) {
63          return row.getParentNode() == getDomNodeOrDie();
64      }
65  
66      /**
67       * Deletes the row at the specified index.
68       * @see <a href="http://msdn.microsoft.com/en-us/library/ms536408.aspx">MSDN Documentation</a>
69       * @param rowIndex the zero-based index of the row to delete
70       */
71      @JsxFunction
72      public void deleteRow(int rowIndex) {
73          final HTMLCollection rows = getRows();
74          final int rowCount = rows.getLength();
75          if (rowIndex == -1) {
76              rowIndex = rowCount - 1;
77          }
78          final boolean rowIndexValid = rowIndex >= 0 && rowIndex < rowCount;
79          if (rowIndexValid) {
80              final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(rowIndex));
81              row.getDomNodeOrDie().remove();
82          }
83      }
84  
85      /**
86       * Inserts a new row at the specified index in the element's row collection. If the index
87       * is -1 or there is no index specified, then the row is appended at the end of the
88       * element's row collection.
89       * @see <a href="http://msdn.microsoft.com/en-us/library/ms536457.aspx">MSDN Documentation</a>
90       * @param index specifies where to insert the row in the rows collection.
91       *        The default value is -1, which appends the new row to the end of the rows collection
92       * @return the newly-created row
93       */
94      @JsxFunction
95      public HtmlUnitScriptable insertRow(final Object index) {
96          int rowIndex = -1;
97          if (!JavaScriptEngine.isUndefined(index)) {
98              rowIndex = (int) JavaScriptEngine.toNumber(index);
99          }
100         final HTMLCollection rows = getRows();
101         final int rowCount = rows.getLength();
102         final int r;
103         if (rowIndex == -1 || rowIndex == rowCount) {
104             r = Math.max(0, rowCount);
105         }
106         else {
107             r = rowIndex;
108         }
109 
110         if (r < 0 || r > rowCount) {
111             throw JavaScriptEngine.asJavaScriptException(
112                     getWindow(),
113                     "Index or size is negative or greater than the allowed amount "
114                             + "(index: " + rowIndex + ", " + rowCount + " rows)",
115                     DOMException.INDEX_SIZE_ERR);
116         }
117 
118         return insertRow(r);
119     }
120 
121     /**
122      * Inserts a new row at the given position.
123      * @param index the index where the row should be inserted (0 &lt;= index &lt;= nbRows)
124      * @return the inserted row
125      */
126     public HtmlUnitScriptable insertRow(final int index) {
127         final HTMLCollection rows = getRows();
128         final int rowCount = rows.getLength();
129         final DomElement newRow = ((HtmlPage) getDomNodeOrDie().getPage()).createElement("tr");
130         if (rowCount == 0) {
131             getDomNodeOrDie().appendChild(newRow);
132         }
133         else if (index == rowCount) {
134             final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(index - 1));
135             row.getDomNodeOrDie().getParentNode().appendChild(newRow);
136         }
137         else {
138             final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(index));
139             // if at the end, then in the same "sub-container" as the last existing row
140             if (index > rowCount - 1) {
141                 row.getDomNodeOrDie().getParentNode().appendChild(newRow);
142             }
143             else {
144                 row.getDomNodeOrDie().insertBefore(newRow);
145             }
146         }
147         return getScriptableFor(newRow);
148     }
149 
150     /**
151      * Returns the value of the {@code align} property.
152      * @return the value of the {@code align} property
153      */
154     @JsxGetter
155     public String getAlign() {
156         return getAlign(true);
157     }
158 
159     /**
160      * Sets the value of the {@code align} property.
161      * @param align the value of the {@code align} property
162      */
163     @JsxSetter
164     public void setAlign(final String align) {
165         setAlign(align, false);
166     }
167 
168 }