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