1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.NoSuchElementException;
24
25 import org.htmlunit.ElementNotFoundException;
26 import org.htmlunit.SgmlPage;
27
28
29
30
31
32
33
34
35
36
37
38 public class HtmlTable extends HtmlElement {
39
40
41 public static final String TAG_NAME = "table";
42
43
44
45
46
47
48
49
50 HtmlTable(final String qualifiedName, final SgmlPage page,
51 final Map<String, DomAttr> attributes) {
52 super(qualifiedName, page, attributes);
53 }
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public final HtmlTableCell getCellAt(final int rowIndex, final int columnIndex) {
77 final RowIterator rowIterator = getRowIterator();
78 final HashSet<Position> occupied = new HashSet<>();
79 int row = 0;
80 for (final HtmlTableRow htmlTableRow : rowIterator) {
81 final HtmlTableRow.CellIterator cellIterator = htmlTableRow.getCellIterator();
82 int col = 0;
83 for (final HtmlTableCell cell : cellIterator) {
84 while (occupied.contains(new Position(row, col))) {
85 col++;
86 }
87 final int nextRow = row + cell.getRowSpan();
88 if (row <= rowIndex && nextRow > rowIndex) {
89 final int nextCol = col + cell.getColumnSpan();
90 if (col <= columnIndex && nextCol > columnIndex) {
91 return cell;
92 }
93 }
94 final int rowSpan = cell.getRowSpan();
95 final int columnSpan = cell.getColumnSpan();
96 if (rowSpan > 1 || columnSpan > 1) {
97 for (int i = 0; i < rowSpan; i++) {
98 for (int j = 0; j < columnSpan; j++) {
99 occupied.add(new Position(row + i, col + j));
100 }
101 }
102 }
103 col++;
104 }
105 row++;
106 }
107 return null;
108 }
109
110
111
112
113 private RowIterator getRowIterator() {
114 return new RowIterator();
115 }
116
117
118
119
120
121 public List<HtmlTableRow> getRows() {
122 final List<HtmlTableRow> result = new ArrayList<>();
123 for (final HtmlTableRow row : getRowIterator()) {
124 result.add(row);
125 }
126 return Collections.unmodifiableList(result);
127 }
128
129
130
131
132
133
134
135 public HtmlTableRow getRow(final int index) throws IndexOutOfBoundsException {
136 int count = 0;
137 for (final HtmlTableRow row : getRowIterator()) {
138 if (count == index) {
139 return row;
140 }
141 count++;
142 }
143 throw new IndexOutOfBoundsException("No row found for index " + index + ".");
144 }
145
146
147
148
149
150
151
152 public final int getRowCount() {
153 int count = 0;
154 for (final RowIterator iterator = getRowIterator(); iterator.hasNext(); iterator.next()) {
155 count++;
156 }
157 return count;
158 }
159
160
161
162
163
164
165
166
167 public final HtmlTableRow getRowById(final String id) throws ElementNotFoundException {
168 for (final HtmlTableRow row : getRowIterator()) {
169 if (row.getId().equals(id)) {
170 return row;
171 }
172 }
173 throw new ElementNotFoundException("tr", DomElement.ID_ATTRIBUTE, id);
174 }
175
176
177
178
179
180
181 public String getCaptionText() {
182 for (final DomElement element : getChildElements()) {
183 if (element instanceof HtmlCaption) {
184 return element.asNormalizedText();
185 }
186 }
187 return null;
188 }
189
190
191
192
193
194
195 public HtmlTableHeader getHeader() {
196 for (final DomElement element : getChildElements()) {
197 if (element instanceof HtmlTableHeader) {
198 return (HtmlTableHeader) element;
199 }
200 }
201 return null;
202 }
203
204
205
206
207
208
209 public HtmlTableFooter getFooter() {
210 for (final DomElement element : getChildElements()) {
211 if (element instanceof HtmlTableFooter) {
212 return (HtmlTableFooter) element;
213 }
214 }
215 return null;
216 }
217
218
219
220
221
222
223
224 public List<HtmlTableBody> getBodies() {
225 final List<HtmlTableBody> bodies = new ArrayList<>();
226 for (final DomElement element : getChildElements()) {
227 if (element instanceof HtmlTableBody) {
228 bodies.add((HtmlTableBody) element);
229 }
230 }
231 return bodies;
232 }
233
234
235
236
237
238
239
240
241
242 public final String getSummaryAttribute() {
243 return getAttributeDirect("summary");
244 }
245
246
247
248
249
250
251
252
253
254 public final String getWidthAttribute() {
255 return getAttributeDirect("width");
256 }
257
258
259
260
261
262
263
264
265
266 public final String getBorderAttribute() {
267 return getAttributeDirect("border");
268 }
269
270
271
272
273
274
275
276
277
278 public final String getFrameAttribute() {
279 return getAttributeDirect("frame");
280 }
281
282
283
284
285
286
287
288
289
290 public final String getRulesAttribute() {
291 return getAttributeDirect("rules");
292 }
293
294
295
296
297
298
299
300
301
302 public final String getCellSpacingAttribute() {
303 return getAttributeDirect("cellspacing");
304 }
305
306
307
308
309
310
311
312
313
314 public final String getCellPaddingAttribute() {
315 return getAttributeDirect("cellpadding");
316 }
317
318
319
320
321
322
323
324
325
326 public final String getAlignAttribute() {
327 return getAttributeDirect("align");
328 }
329
330
331
332
333
334
335
336
337
338 public final String getBgcolorAttribute() {
339 return getAttributeDirect("bgcolor");
340 }
341
342
343
344
345
346 private class RowIterator implements Iterator<HtmlTableRow>, Iterable<HtmlTableRow> {
347 private HtmlTableRow nextRow_;
348 private TableRowGroup currentGroup_;
349
350
351 RowIterator() {
352 setNextRow(getFirstChild());
353 }
354
355
356
357
358 @Override
359 public boolean hasNext() {
360 return nextRow_ != null;
361 }
362
363
364
365
366
367 @Override
368 public HtmlTableRow next() throws NoSuchElementException {
369 return nextRow();
370 }
371
372
373
374
375 @Override
376 public void remove() {
377 if (nextRow_ == null) {
378 throw new IllegalStateException();
379 }
380 final DomNode sibling = nextRow_.getPreviousSibling();
381 if (sibling != null) {
382 sibling.remove();
383 }
384 }
385
386
387
388
389
390 public HtmlTableRow nextRow() throws NoSuchElementException {
391 if (nextRow_ != null) {
392 final HtmlTableRow result = nextRow_;
393 setNextRow(nextRow_.getNextSibling());
394 return result;
395 }
396 throw new NoSuchElementException();
397 }
398
399
400
401
402
403
404 private void setNextRow(final DomNode node) {
405 nextRow_ = null;
406 for (DomNode next = node; next != null; next = next.getNextSibling()) {
407 if (next instanceof HtmlTableRow) {
408 nextRow_ = (HtmlTableRow) next;
409 return;
410 }
411 else if (currentGroup_ == null && next instanceof TableRowGroup) {
412 currentGroup_ = (TableRowGroup) next;
413 setNextRow(next.getFirstChild());
414 return;
415 }
416 }
417 if (currentGroup_ != null) {
418 final DomNode group = currentGroup_;
419 currentGroup_ = null;
420 setNextRow(group.getNextSibling());
421 }
422 }
423
424 @Override
425 public Iterator<HtmlTableRow> iterator() {
426 return this;
427 }
428 }
429
430
431
432
433
434 @Override
435 protected boolean isEmptyXmlTagExpanded() {
436 return true;
437 }
438
439
440
441
442 @Override
443 public DisplayStyle getDefaultStyleDisplay() {
444 return DisplayStyle.TABLE;
445 }
446
447 private static final class Position {
448
449 private final int posX_;
450 private final int posY_;
451
452 Position(final int x, final int y) {
453 posX_ = x;
454 posY_ = y;
455 }
456
457 @Override
458 public int hashCode() {
459 final int prime = 31;
460 int result = 1;
461 result = prime * result + posX_;
462 result = prime * result + posY_;
463 return result;
464 }
465
466 @Override
467 public boolean equals(final Object obj) {
468 if (this == obj) {
469 return true;
470 }
471 if (obj == null) {
472 return false;
473 }
474 if (getClass() != obj.getClass()) {
475 return false;
476 }
477
478 final Position other = (Position) obj;
479 if (posX_ != other.posX_) {
480 return false;
481 }
482 if (posY_ != other.posY_) {
483 return false;
484 }
485
486 return true;
487 }
488 }
489 }