1 /*
2 * Copyright (c) 2002-2026 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.html;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.NoSuchElementException;
23
24 import org.htmlunit.SgmlPage;
25
26 /**
27 * Wrapper for the HTML element "tr".
28 *
29 * @author Mike Bowler
30 * @author David K. Taylor
31 * @author Christian Sell
32 * @author Ahmed Ashour
33 * @author Ronald Brill
34 * @author Frank Danek
35 */
36 public class HtmlTableRow extends HtmlElement {
37
38 /** The HTML tag represented by this element. */
39 public static final String TAG_NAME = "tr";
40
41 /**
42 * Creates an instance.
43 *
44 * @param qualifiedName the qualified name of the element type to instantiate
45 * @param page the page that this element is contained within
46 * @param attributes the initial attributes
47 */
48 HtmlTableRow(final String qualifiedName, final SgmlPage page,
49 final Map<String, DomAttr> attributes) {
50 super(qualifiedName, page, attributes);
51 }
52
53 /**
54 * Returns an iterator over all cells in this row.
55 *
56 * @return an iterator over all {@link HtmlTableCell} objects in this row
57 */
58 public CellIterator getCellIterator() {
59 return new CellIterator();
60 }
61
62 /**
63 * Returns an immutable list of all cells in this row.
64 *
65 * @return an immutable list containing all {@link HtmlTableCell} objects in this row
66 * @see #getCellIterator()
67 */
68 public List<HtmlTableCell> getCells() {
69 final List<HtmlTableCell> result = new ArrayList<>();
70 for (final HtmlTableCell cell : getCellIterator()) {
71 result.add(cell);
72 }
73 return Collections.unmodifiableList(result);
74 }
75
76 /**
77 * Returns the cell at the specified index.
78 *
79 * @param index the 0-based index
80 * @return the cell at the given index
81 * @throws IndexOutOfBoundsException if there is no cell at the given index
82 */
83 public HtmlTableCell getCell(final int index) throws IndexOutOfBoundsException {
84 int count = 0;
85 for (final HtmlTableCell cell : getCellIterator()) {
86 if (count == index) {
87 return cell;
88 }
89 count++;
90 }
91 throw new IndexOutOfBoundsException("No cell found for index " + index + ".");
92 }
93
94 /**
95 * Returns the value of the attribute {@code align}. Refer to the
96 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
97 * documentation for details on the use of this attribute.
98 *
99 * @return the value of the attribute {@code align}
100 * or an empty string if that attribute isn't defined.
101 */
102 public final String getAlignAttribute() {
103 return getAttributeDirect("align");
104 }
105
106 /**
107 * Returns the value of the attribute {@code char}. Refer to the
108 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
109 * documentation for details on the use of this attribute.
110 *
111 * @return the value of the attribute {@code char}
112 * or an empty string if that attribute isn't defined.
113 */
114 public final String getCharAttribute() {
115 return getAttributeDirect("char");
116 }
117
118 /**
119 * Returns the value of the attribute {@code charoff}. Refer to the
120 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
121 * documentation for details on the use of this attribute.
122 *
123 * @return the value of the attribute {@code charoff}
124 * or an empty string if that attribute isn't defined.
125 */
126 public final String getCharoffAttribute() {
127 return getAttributeDirect("charoff");
128 }
129
130 /**
131 * Returns the value of the attribute {@code valign}. Refer to the
132 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
133 * documentation for details on the use of this attribute.
134 *
135 * @return the value of the attribute {@code valign}
136 * or an empty string if that attribute isn't defined.
137 */
138 public final String getValignAttribute() {
139 return getAttributeDirect("valign");
140 }
141
142 /**
143 * Returns the table containing this row.
144 *
145 * @return the enclosing table
146 */
147 public HtmlTable getEnclosingTable() {
148 return (HtmlTable) getEnclosingElement("table");
149 }
150
151 /**
152 * Returns the value of the attribute {@code bgcolor}. Refer to the
153 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
154 * documentation for details on the use of this attribute.
155 *
156 * @return the value of the attribute {@code bgcolor}
157 * or an empty string if that attribute isn't defined.
158 */
159 public final String getBgcolorAttribute() {
160 return getAttributeDirect("bgcolor");
161 }
162
163 /**
164 * An Iterator over the HtmlTableCells contained in this row. It will also dive
165 * into nested forms, even though that is illegal HTML.
166 */
167 public class CellIterator implements Iterator<HtmlTableCell>, Iterable<HtmlTableCell> {
168 private HtmlTableCell nextCell_;
169 private HtmlForm currentForm_;
170
171 /** Creates an instance. */
172 public CellIterator() {
173 setNextCell(getFirstChild());
174 }
175
176 /**
177 * {@inheritDoc}
178 */
179 @Override
180 public boolean hasNext() {
181 return nextCell_ != null;
182 }
183
184 /**
185 * {@inheritDoc}
186 */
187 @Override
188 public HtmlTableCell next() throws NoSuchElementException {
189 return nextCell();
190 }
191
192 /**
193 * {@inheritDoc}
194 */
195 @Override
196 public void remove() {
197 if (nextCell_ == null) {
198 throw new IllegalStateException();
199 }
200 final DomNode sibling = nextCell_.getPreviousSibling();
201 if (sibling != null) {
202 sibling.remove();
203 }
204 }
205
206 /**
207 * Returns the next cell.
208 *
209 * @return the next cell
210 * @throws NoSuchElementException if no cell is available
211 */
212 public HtmlTableCell nextCell() throws NoSuchElementException {
213 if (nextCell_ != null) {
214 final HtmlTableCell result = nextCell_;
215 setNextCell(nextCell_.getNextSibling());
216 return result;
217 }
218 throw new NoSuchElementException();
219 }
220
221 /**
222 * Sets the internal position to the next cell, starting at the given node.
223 * @param node the node to mark as the next cell; if this is not a cell, the
224 * next reachable cell will be marked.
225 */
226 private void setNextCell(final DomNode node) {
227 nextCell_ = null;
228 for (DomNode next = node; next != null; next = next.getNextSibling()) {
229 if (next instanceof HtmlTableCell cell) {
230 nextCell_ = cell;
231 return;
232 }
233 else if (currentForm_ == null && next instanceof HtmlForm form) {
234 // Completely illegal HTML but some of the big sites (ie amazon) do this
235 currentForm_ = form;
236 setNextCell(next.getFirstChild());
237 return;
238 }
239 }
240 if (currentForm_ != null) {
241 final DomNode form = currentForm_;
242 currentForm_ = null;
243 setNextCell(form.getNextSibling());
244 }
245 }
246
247 /**
248 * {@inheritDoc}
249 */
250 @Override
251 public Iterator<HtmlTableCell> iterator() {
252 return this;
253 }
254 }
255
256 /**
257 * {@inheritDoc}
258 */
259 @Override
260 public DisplayStyle getDefaultStyleDisplay() {
261 return DisplayStyle.TABLE_ROW;
262 }
263 }