001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.html;
039:
040: import java.util.ArrayList;
041: import java.util.Collections;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Map;
045: import java.util.NoSuchElementException;
046:
047: import com.gargoylesoftware.htmlunit.ElementNotFoundException;
048:
049: /**
050: * Wrapper for the HTML element "table".
051: *
052: * @version $Revision: 2132 $
053: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
054: * @author David K. Taylor
055: * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
056: * @author Ahmed Ashour
057: */
058: public class HtmlTable extends ClickableElement {
059:
060: private static final long serialVersionUID = 2484055580262042798L;
061:
062: /** the HTML tag represented by this element */
063: public static final String TAG_NAME = "table";
064:
065: /**
066: * Create an instance
067: *
068: * @param page The page that contains this element
069: * @param attributes the initial attributes
070: * @deprecated You should not directly construct HtmlTable.
071: */
072: //TODO: to be removed, deprecated after 1.11
073: public HtmlTable(final HtmlPage page, final Map attributes) {
074: this (null, TAG_NAME, page, attributes);
075: }
076:
077: /**
078: * Create an instance
079: *
080: * @param namespaceURI the URI that identifies an XML namespace.
081: * @param qualifiedName The qualified name of the element type to instantiate
082: * @param page The page that contains this element
083: * @param attributes the initial attributes
084: */
085: HtmlTable(final String namespaceURI, final String qualifiedName,
086: final HtmlPage page, final Map attributes) {
087: super (namespaceURI, qualifiedName, page, attributes);
088: }
089:
090: /**
091: * Return the first cell that matches the specified row and column, searching left to right, top to bottom.
092: *
093: * @param rowIndex The row index
094: * @param columnIndex The column index
095: * @return The HtmlTableCell at that location or null if there are no cells at that location
096: */
097: public final HtmlTableCell getCellAt(final int rowIndex,
098: final int columnIndex) {
099: final RowIterator rowIterator = getRowIterator();
100: for (int rowNo = 0; rowIterator.hasNext(); rowNo++) {
101: final HtmlTableRow row = rowIterator.nextRow();
102: final HtmlTableRow.CellIterator cellIterator = row
103: .getCellIterator();
104: for (int colNo = 0; cellIterator.hasNext(); colNo++) {
105: final HtmlTableCell cell = cellIterator.nextCell();
106: if (rowNo <= rowIndex
107: && rowNo + cell.getRowSpan() > rowIndex) {
108: if (colNo <= columnIndex
109: && colNo + cell.getColumnSpan() > columnIndex) {
110: return cell;
111: }
112: }
113: }
114: }
115: return null;
116: }
117:
118: /**
119: * @return an iterator over all the HtmlTableRow objects
120: */
121: private RowIterator getRowIterator() {
122: return new RowIterator();
123: }
124:
125: /**
126: * @return an immutable list containing all the HtmlTableRow objects
127: * @see #getRowIterator
128: */
129: public List getRows() {
130: final List result = new ArrayList();
131: for (final RowIterator iterator = getRowIterator(); iterator
132: .hasNext();) {
133: result.add(iterator.next());
134: }
135: return Collections.unmodifiableList(result);
136: }
137:
138: /**
139: * @param index the 0-based index of the row
140: * @return the HtmlTableRow at the given index
141: * @throws IndexOutOfBoundsException if there is no row at the given index
142: * @see #getRowIterator
143: */
144: public HtmlTableRow getRow(final int index)
145: throws IndexOutOfBoundsException {
146: int count = 0;
147: for (final RowIterator iterator = getRowIterator(); iterator
148: .hasNext(); count++) {
149: final HtmlTableRow next = iterator.nextRow();
150: if (count == index) {
151: return next;
152: }
153: }
154: throw new IndexOutOfBoundsException();
155: }
156:
157: /**
158: * compute the number of rows in this table. Note that the count is computed dynamically
159: * by iterating over all rows
160: *
161: * @return The number of rows in this table
162: */
163: public final int getRowCount() {
164: int count = 0;
165: for (final RowIterator iterator = getRowIterator(); iterator
166: .hasNext(); iterator.next()) {
167: count++;
168: }
169: return count;
170: }
171:
172: /**
173: * Find and return the row with the specified id.
174: *
175: * @param id The id of the row
176: * @return The row with the specified id.
177: * @exception ElementNotFoundException If the row cannot be found.
178: */
179: public final HtmlTableRow getRowById(final String id)
180: throws ElementNotFoundException {
181: final RowIterator iterator = new RowIterator();
182: while (iterator.hasNext()) {
183: final HtmlTableRow row = (HtmlTableRow) iterator.next();
184: if (row.getIdAttribute().equals(id)) {
185: return row;
186: }
187: }
188: throw new ElementNotFoundException("tr", "id", id);
189: }
190:
191: /**
192: * Return the table caption text or an empty string if a caption wasn't specified
193: *
194: * @return The caption text
195: */
196: public String getCaptionText() {
197: final Iterator iterator = getChildElementsIterator();
198: while (iterator.hasNext()) {
199: final HtmlElement element = (HtmlElement) iterator.next();
200: if (element instanceof HtmlCaption) {
201: return element.asText();
202: }
203: }
204: return null;
205: }
206:
207: /**
208: * Return the table header or null if a header wasn't specified
209: *
210: * @return The table header
211: */
212: public HtmlTableHeader getHeader() {
213: final Iterator iterator = getChildElementsIterator();
214: while (iterator.hasNext()) {
215: final HtmlElement element = (HtmlElement) iterator.next();
216: if (element instanceof HtmlTableHeader) {
217: return (HtmlTableHeader) element;
218: }
219: }
220: return null;
221: }
222:
223: /**
224: * Return the table footer or null if a footer wasn't specified
225: *
226: * @return The table footer
227: */
228: public HtmlTableFooter getFooter() {
229: final Iterator iterator = getChildElementsIterator();
230: while (iterator.hasNext()) {
231: final HtmlElement element = (HtmlElement) iterator.next();
232: if (element instanceof HtmlTableFooter) {
233: return (HtmlTableFooter) element;
234: }
235: }
236: return null;
237: }
238:
239: /**
240: * Return a list of tables bodies defined in this table. If no bodies were defined
241: * then an empty list will be returned.
242: *
243: * @return A list of {@link com.gargoylesoftware.htmlunit.html.HtmlTableBody} objects.
244: */
245: public List getBodies() {
246: final List bodies = new ArrayList();
247: final Iterator iterator = getChildElementsIterator();
248: while (iterator.hasNext()) {
249: final HtmlElement element = (HtmlElement) iterator.next();
250: if (element instanceof HtmlTableBody) {
251: bodies.add(element);
252: }
253: }
254: return bodies;
255: }
256:
257: /**
258: * Return the value of the attribute "summary". Refer to the
259: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
260: * documentation for details on the use of this attribute.
261: *
262: * @return The value of the attribute "summary"
263: * or an empty string if that attribute isn't defined.
264: */
265: public final String getSummaryAttribute() {
266: return getAttributeValue("summary");
267: }
268:
269: /**
270: * Return the value of the attribute "width". Refer to the
271: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
272: * documentation for details on the use of this attribute.
273: *
274: * @return The value of the attribute "width"
275: * or an empty string if that attribute isn't defined.
276: */
277: public final String getWidthAttribute() {
278: return getAttributeValue("width");
279: }
280:
281: /**
282: * Return the value of the attribute "border". Refer to the
283: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
284: * documentation for details on the use of this attribute.
285: *
286: * @return The value of the attribute "border"
287: * or an empty string if that attribute isn't defined.
288: */
289: public final String getBorderAttribute() {
290: return getAttributeValue("border");
291: }
292:
293: /**
294: * Return the value of the attribute "frame". Refer to the
295: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
296: * documentation for details on the use of this attribute.
297: *
298: * @return The value of the attribute "frame"
299: * or an empty string if that attribute isn't defined.
300: */
301: public final String getFrameAttribute() {
302: return getAttributeValue("frame");
303: }
304:
305: /**
306: * Return the value of the attribute "rules". Refer to the
307: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
308: * documentation for details on the use of this attribute.
309: *
310: * @return The value of the attribute "rules"
311: * or an empty string if that attribute isn't defined.
312: */
313: public final String getRulesAttribute() {
314: return getAttributeValue("rules");
315: }
316:
317: /**
318: * Return the value of the attribute "cellspacing". Refer to the
319: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
320: * documentation for details on the use of this attribute.
321: *
322: * @return The value of the attribute "cellspacing"
323: * or an empty string if that attribute isn't defined.
324: */
325: public final String getCellSpacingAttribute() {
326: return getAttributeValue("cellspacing");
327: }
328:
329: /**
330: * Return the value of the attribute "cellpadding". Refer to the
331: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
332: * documentation for details on the use of this attribute.
333: *
334: * @return The value of the attribute "cellpadding"
335: * or an empty string if that attribute isn't defined.
336: */
337: public final String getCellPaddingAttribute() {
338: return getAttributeValue("cellpadding");
339: }
340:
341: /**
342: * Return the value of the attribute "align". Refer to the
343: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
344: * documentation for details on the use of this attribute.
345: *
346: * @return The value of the attribute "align"
347: * or an empty string if that attribute isn't defined.
348: */
349: public final String getAlignAttribute() {
350: return getAttributeValue("align");
351: }
352:
353: /**
354: * Return the value of the attribute "bgcolor". Refer to the
355: * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
356: * documentation for details on the use of this attribute.
357: *
358: * @return The value of the attribute "bgcolor"
359: * or an empty string if that attribute isn't defined.
360: */
361: public final String getBgcolorAttribute() {
362: return getAttributeValue("bgcolor");
363: }
364:
365: /**
366: * an iterator that moves over all rows in this table. The iterator will also
367: * enter into nested row group elements (header, footer and body)
368: */
369: private class RowIterator implements Iterator {
370: private HtmlTableRow nextRow_;
371: private TableRowGroup currentGroup_;
372:
373: /** create a new instance */
374: public RowIterator() {
375: setNextRow(getFirstDomChild());
376: }
377:
378: /**
379: * @return <code>true</code> if there are more rows available
380: */
381: public boolean hasNext() {
382: return nextRow_ != null;
383: }
384:
385: /**
386: * @return the next row from this iterator
387: * @throws NoSuchElementException if no more rows are available
388: */
389: public Object next() throws NoSuchElementException {
390: return nextRow();
391: }
392:
393: /**
394: * remove the current row from the underlying table
395: * @throws IllegalStateException if there is no current element
396: */
397: public void remove() throws IllegalStateException {
398: if (nextRow_ == null) {
399: throw new IllegalStateException();
400: }
401: final DomNode sibling = nextRow_.getPreviousDomSibling();
402: if (sibling != null) {
403: sibling.remove();
404: }
405: }
406:
407: /**
408: * @return the next row from this iterator
409: * @throws NoSuchElementException if no more rows are available
410: */
411: public HtmlTableRow nextRow() throws NoSuchElementException {
412: if (nextRow_ != null) {
413: final HtmlTableRow result = nextRow_;
414: setNextRow(nextRow_.getNextDomSibling());
415: return result;
416: } else {
417: throw new NoSuchElementException();
418: }
419: }
420:
421: /**
422: * set the internal position to the next row, starting at the given node
423: * @param node the node to mark as the next row. If this is not a row, the
424: * next reachable row will be marked
425: */
426: private void setNextRow(final DomNode node) {
427:
428: nextRow_ = null;
429: for (DomNode next = node; next != null; next = next
430: .getNextDomSibling()) {
431: if (next instanceof HtmlTableRow) {
432: nextRow_ = (HtmlTableRow) next;
433: return;
434: } else if (currentGroup_ == null
435: && next instanceof TableRowGroup) {
436: currentGroup_ = (TableRowGroup) next;
437: setNextRow(next.getFirstDomChild());
438: return;
439: }
440: }
441: if (currentGroup_ != null) {
442: final DomNode group = currentGroup_;
443: currentGroup_ = null;
444: setNextRow(group.getNextDomSibling());
445: }
446: }
447: }
448: }
|