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.ArrayList;
19  import java.util.List;
20  import java.util.function.Supplier;
21  
22  import org.htmlunit.html.DomNode;
23  import org.htmlunit.html.HtmlElement;
24  import org.htmlunit.html.HtmlTable;
25  import org.htmlunit.html.HtmlTableBody;
26  import org.htmlunit.html.HtmlTableFooter;
27  import org.htmlunit.html.HtmlTableHeader;
28  import org.htmlunit.html.HtmlTableRow;
29  import org.htmlunit.javascript.HtmlUnitScriptable;
30  import org.htmlunit.javascript.JavaScriptEngine;
31  import org.htmlunit.javascript.configuration.JsxClass;
32  import org.htmlunit.javascript.configuration.JsxConstructor;
33  import org.htmlunit.javascript.configuration.JsxFunction;
34  import org.htmlunit.javascript.configuration.JsxGetter;
35  import org.htmlunit.javascript.configuration.JsxSetter;
36  import org.htmlunit.javascript.host.dom.Node;
37  
38  /**
39   * The JavaScript object {@code HTMLTableElement}.
40   *
41   * @author David D. Kilzer
42   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
43   * @author Daniel Gredler
44   * @author Chris Erskine
45   * @author Marc Guillemot
46   * @author Ahmed Ashour
47   * @author Ronald Brill
48   * @author Frank Danek
49   */
50  @JsxClass(domClass = HtmlTable.class)
51  public class HTMLTableElement extends RowContainer {
52  
53      /**
54       * JavaScript constructor.
55       */
56      @Override
57      @JsxConstructor
58      public void jsConstructor() {
59          super.jsConstructor();
60      }
61  
62      /**
63       * Returns the table's caption element, or {@code null} if none exists. If more than one
64       * caption is declared in the table, this method returns the first one.
65       * @return the table's caption element
66       */
67      @JsxGetter
68      public HtmlUnitScriptable getCaption() {
69          final List<HtmlElement> captions = getDomNodeOrDie().getStaticElementsByTagName("caption");
70          if (captions.isEmpty()) {
71              return null;
72          }
73          return getScriptableFor(captions.get(0));
74      }
75  
76      /**
77       * Sets the caption.
78       * @param o the caption
79       */
80      @JsxSetter
81      public void setCaption(final Object o) {
82          if (!(o instanceof HTMLTableCaptionElement)) {
83              throw JavaScriptEngine.typeError("Not a caption");
84          }
85  
86          // remove old caption (if any)
87          deleteCaption();
88  
89          final HTMLTableCaptionElement caption = (HTMLTableCaptionElement) o;
90          getDomNodeOrDie().appendChild(caption.getDomNodeOrDie());
91      }
92  
93      /**
94       * Returns the table's tfoot element, or {@code null} if none exists. If more than one
95       * tfoot is declared in the table, this method returns the first one.
96       * @return the table's tfoot element
97       */
98      @JsxGetter
99      public HtmlUnitScriptable getTFoot() {
100         final List<HtmlElement> tfoots = getDomNodeOrDie().getStaticElementsByTagName("tfoot");
101         if (tfoots.isEmpty()) {
102             return null;
103         }
104         return getScriptableFor(tfoots.get(0));
105     }
106 
107     /**
108      * Sets the tFoot.
109      * @param o the tFoot
110      */
111     @JsxSetter
112     public void setTFoot(final Object o) {
113         if (!(o instanceof HTMLTableSectionElement
114             && "TFOOT".equals(((HTMLTableSectionElement) o).getTagName()))) {
115             throw JavaScriptEngine.typeError("Not a tFoot");
116         }
117 
118         // remove old caption (if any)
119         deleteTFoot();
120 
121         final HTMLTableSectionElement tfoot = (HTMLTableSectionElement) o;
122         getDomNodeOrDie().appendChild(tfoot.getDomNodeOrDie());
123     }
124 
125     /**
126      * Returns the table's thead element, or {@code null} if none exists. If more than one
127      * thead is declared in the table, this method returns the first one.
128      * @return the table's thead element
129      */
130     @JsxGetter
131     public HtmlUnitScriptable getTHead() {
132         final List<HtmlElement> theads = getDomNodeOrDie().getStaticElementsByTagName("thead");
133         if (theads.isEmpty()) {
134             return null;
135         }
136         return getScriptableFor(theads.get(0));
137     }
138 
139     /**
140      * Sets the {@code tHead}.
141      * @param o the {@code tHead}
142      */
143     @JsxSetter
144     public void setTHead(final Object o) {
145         if (!(o instanceof HTMLTableSectionElement
146             && "THEAD".equals(((HTMLTableSectionElement) o).getTagName()))) {
147             throw JavaScriptEngine.typeError("Not a tHead");
148         }
149 
150         // remove old caption (if any)
151         deleteTHead();
152 
153         final HTMLTableSectionElement thead = (HTMLTableSectionElement) o;
154         getDomNodeOrDie().appendChild(thead.getDomNodeOrDie());
155     }
156 
157     /**
158      * Returns the tbody's in the table.
159      * @return the tbody's in the table
160      */
161     @JsxGetter
162     public HtmlUnitScriptable getTBodies() {
163         final HtmlTable table = (HtmlTable) getDomNodeOrDie();
164         final HTMLCollection bodies = new HTMLCollection(table, false);
165         bodies.setElementsSupplier((Supplier<List<DomNode>> & Serializable) () -> new ArrayList<>(table.getBodies()));
166         return bodies;
167     }
168 
169     /**
170      * If this table does not have a caption, this method creates an empty table caption,
171      * adds it to the table and then returns it. If one or more captions already exist,
172      * this method returns the first existing caption.
173      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536381.aspx">MSDN Documentation</a>
174      * @return a newly added caption if no caption exists, or the first existing caption
175      */
176     @JsxFunction
177     public HtmlUnitScriptable createCaption() {
178         return getScriptableFor(getDomNodeOrDie().appendChildIfNoneExists("caption"));
179     }
180 
181     /**
182      * If this table does not have a tfoot element, this method creates an empty tfoot
183      * element, adds it to the table and then returns it. If this table already has a
184      * tfoot element, this method returns the existing tfoot element.
185      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536402.aspx">MSDN Documentation</a>
186      * @return a newly added caption if no caption exists, or the first existing caption
187      */
188     @JsxFunction
189     public HtmlUnitScriptable createTFoot() {
190         return getScriptableFor(getDomNodeOrDie().appendChildIfNoneExists("tfoot"));
191     }
192 
193     /**
194      * If this table does not have a tbody element, this method creates an empty tbody
195      * element, adds it to the table and then returns it. If this table already has a
196      * tbody element, this method returns the existing tbody element.
197      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536402.aspx">MSDN Documentation</a>
198      * @return a newly added caption if no caption exists, or the first existing caption
199      */
200     @JsxFunction
201     public HtmlUnitScriptable createTBody() {
202         return getScriptableFor(getDomNodeOrDie().appendChildIfNoneExists("tbody"));
203     }
204 
205     /**
206      * If this table does not have a thead element, this method creates an empty
207      * thead element, adds it to the table and then returns it. If this table
208      * already has a thead element, this method returns the existing thead element.
209      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536403.aspx">MSDN Documentation</a>
210      * @return a newly added caption if no caption exists, or the first existing caption
211      */
212     @JsxFunction
213     public HtmlUnitScriptable createTHead() {
214         return getScriptableFor(getDomNodeOrDie().appendChildIfNoneExists("thead"));
215     }
216 
217     /**
218      * Deletes this table's caption. If the table has multiple captions, this method
219      * deletes only the first caption. If this table does not have any captions, this
220      * method does nothing.
221      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536405.aspx">MSDN Documentation</a>
222      */
223     @JsxFunction
224     public void deleteCaption() {
225         getDomNodeOrDie().removeChild("caption", 0);
226     }
227 
228     /**
229      * Deletes this table's tfoot element. If the table has multiple tfoot elements, this
230      * method deletes only the first tfoot element. If this table does not have any tfoot
231      * elements, this method does nothing.
232      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536409.aspx">MSDN Documentation</a>
233      */
234     @JsxFunction
235     public void deleteTFoot() {
236         getDomNodeOrDie().removeChild("tfoot", 0);
237     }
238 
239     /**
240      * Deletes this table's thead element. If the table has multiple thead elements, this
241      * method deletes only the first thead element. If this table does not have any thead
242      * elements, this method does nothing.
243      * @see <a href="http://msdn.microsoft.com/en-us/library/ms536410.aspx">MSDN Documentation</a>
244      */
245     @JsxFunction
246     public void deleteTHead() {
247         getDomNodeOrDie().removeChild("thead", 0);
248     }
249 
250     /**
251      * Indicates if the row belongs to this container.
252      * @param row the row to test
253      * @return {@code true} if it belongs to this container
254      */
255     @Override
256     protected boolean isContainedRow(final HtmlTableRow row) {
257         final DomNode parent = row.getParentNode(); // the tbody, thead or tfoo
258         return parent != null
259                 && parent.getParentNode() == getDomNodeOrDie();
260     }
261 
262     /**
263      * Handle special case where table is empty.
264      * {@inheritDoc}
265      */
266     @Override
267     public HtmlUnitScriptable insertRow(final int index) {
268         // check if a tbody should be created
269         if (index != 0) {
270             for (final HtmlElement htmlElement : getDomNodeOrDie().getHtmlElementDescendants()) {
271                 if (htmlElement instanceof HtmlTableBody
272                         || htmlElement instanceof HtmlTableHeader
273                         || htmlElement instanceof HtmlTableFooter) {
274                     return super.insertRow(index);
275                 }
276             }
277         }
278 
279         final HtmlElement tBody = getDomNodeOrDie().appendChildIfNoneExists("tbody");
280         return ((RowContainer) getScriptableFor(tBody)).insertRow(0);
281     }
282 
283     /**
284      * Returns the {@code width} property.
285      * @return the {@code width} property
286      */
287     @JsxGetter(propertyName = "width")
288     public String getWidth_js() {
289         return getDomNodeOrDie().getAttributeDirect("width");
290     }
291 
292     /**
293      * Sets the {@code width} property.
294      * @param width the {@code width} property
295      */
296     @JsxSetter(propertyName = "width")
297     public void setWidth_js(final String width) {
298         getDomNodeOrDie().setAttribute("width", width);
299     }
300 
301     /**
302      * Returns the {@code cellSpacing} property.
303      * @return the {@code cellSpacing} property
304      */
305     @JsxGetter
306     public String getCellSpacing() {
307         return getDomNodeOrDie().getAttributeDirect("cellspacing");
308     }
309 
310     /**
311      * Sets the {@code cellSpacing} property.
312      * @param cellSpacing the {@code cellSpacing} property
313      */
314     @JsxSetter
315     public void setCellSpacing(final String cellSpacing) {
316         getDomNodeOrDie().setAttribute("cellspacing", cellSpacing);
317     }
318 
319     /**
320      * Returns the {@code cellPadding} property.
321      * @return the {@code cellPadding} property
322      */
323     @JsxGetter
324     public String getCellPadding() {
325         return getDomNodeOrDie().getAttributeDirect("cellpadding");
326     }
327 
328     /**
329      * Sets the {@code cellPadding} property.
330      * @param cellPadding the {@code cellPadding} property
331      */
332     @JsxSetter
333     public void setCellPadding(final String cellPadding) {
334         getDomNodeOrDie().setAttribute("cellpadding", cellPadding);
335     }
336 
337     /**
338      * Gets the {@code border} property.
339      * @return the {@code border} property
340      */
341     @JsxGetter
342     public String getBorder() {
343         return getDomNodeOrDie().getAttributeDirect("border");
344     }
345 
346     /**
347      * Sets the {@code border} property.
348      * @param border the {@code border} property
349      */
350     @JsxSetter
351     public void setBorder(final String border) {
352         getDomNodeOrDie().setAttribute("border", border);
353     }
354 
355     /**
356      * Returns the value of the {@code bgColor} property.
357      * @return the value of the {@code bgColor} property
358      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533505.aspx">MSDN Documentation</a>
359      */
360     @JsxGetter
361     public String getBgColor() {
362         return getDomNodeOrDie().getAttribute("bgColor");
363     }
364 
365     /**
366      * Sets the value of the {@code bgColor} property.
367      * @param bgColor the value of the {@code bgColor} property
368      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533505.aspx">MSDN Documentation</a>
369      */
370     @JsxSetter
371     public void setBgColor(final String bgColor) {
372         setColorAttribute("bgColor", bgColor);
373     }
374 
375     /**
376      * {@inheritDoc}
377      */
378     @Override
379     public Node appendChild(final Object childObject) {
380         final Node appendedChild = super.appendChild(childObject);
381         getDomNodeOrDie().getPage().clearComputedStyles(getDomNodeOrDie());
382         return appendedChild;
383     }
384 
385     /**
386      * {@inheritDoc}
387      */
388     @Override
389     public Node removeChild(final Object childObject) {
390         final Node removedChild = super.removeChild(childObject);
391         getDomNodeOrDie().getPage().clearComputedStyles(getDomNodeOrDie());
392         return removedChild;
393     }
394 
395     /**
396      * Gets the {@code summary} property.
397      * @return the property
398      */
399     @JsxGetter
400     public String getSummary() {
401         return getDomNodeOrDie().getAttributeDirect("summary");
402     }
403 
404     /**
405      * Sets the {@code summary} property.
406      * @param summary the new property
407      */
408     @JsxSetter
409     public void setSummary(final String summary) {
410         setAttribute("summary", summary);
411     }
412 
413     /**
414      * Gets the {@code rules} property.
415      * @return the property
416      */
417     @JsxGetter
418     public String getRules() {
419         return getDomNodeOrDie().getAttributeDirect("rules");
420     }
421 
422     /**
423      * Sets the {@code rules} property.
424      * @param rules the new property
425      */
426     @JsxSetter
427     public void setRules(final String rules) {
428         setAttribute("rules", rules);
429     }
430 
431 }