001 /*
002 * Copyright (c) 2006 Henri Sivonen
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a
005 * copy of this software and associated documentation files (the "Software"),
006 * to deal in the Software without restriction, including without limitation
007 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
008 * and/or sell copies of the Software, and to permit persons to whom the
009 * Software is furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in
012 * all copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
017 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020 * DEALINGS IN THE SOFTWARE.
021 */
022
023 package org.whattf.checker.table;
024
025 import java.util.LinkedList;
026
027 import org.whattf.checker.AttributeUtil;
028 import org.whattf.checker.Checker;
029 import org.xml.sax.Attributes;
030 import org.xml.sax.SAXException;
031
032
033 /**
034 * Checks XHTML table integrity: overlapping cells, spanning past the end
035 * of row group, etc.
036 *
037 * @version $Id: TableChecker.java 169 2007-05-25 07:21:55Z hsivonen $
038 * @author hsivonen
039 */
040 public final class TableChecker extends Checker {
041
042 /**
043 * Constructor.
044 */
045 public TableChecker() {
046 super();
047 }
048
049 /**
050 * Holds the current table. (Premature optimization to avoid
051 * peeking the top of the stack all the time.)
052 */
053 private Table current;
054
055 /**
056 * A stack for holding the tables that are open and ancestors of
057 * the current table. Grows from the tail.
058 */
059 private final LinkedList<Table> stack = new LinkedList<Table>();
060
061 /**
062 * Pushes the current table onto the stack and creates a new one.
063 */
064 private void push() {
065 if (current != null) {
066 stack.addLast(current);
067 }
068 current = new Table(this);
069 }
070
071 /**
072 * Ends the current table, discards it and pops the top of the
073 * stack to be the new current table.
074 *
075 * @throws SAXException if ending the table throws
076 */
077 private void pop() throws SAXException {
078 if (current == null) {
079 throw new IllegalStateException("Bug!");
080 }
081 current.end();
082 if (stack.isEmpty()) {
083 current = null;
084 } else {
085 current = stack.removeLast();
086 }
087 }
088
089 /**
090 * @see org.whattf.checker.Checker#startElement(java.lang.String,
091 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
092 */
093 public void startElement(String uri, String localName, String qName,
094 Attributes atts) throws SAXException {
095 if ("http://www.w3.org/1999/xhtml".equals(uri)) {
096 if ("table".equals(localName)) {
097 push();
098 } else if (current != null) {
099 if ("td".equals(localName)) {
100 current.startCell(false, atts);
101 } else if ("th".equals(localName)) {
102 current.startCell(true, atts);
103 } else if ("tr".equals(localName)) {
104 current.startRow();
105 } else if ("tbody".equals(localName)
106 || "thead".equals(localName)
107 || "tfoot".equals(localName)) {
108 current.startRowGroup(localName);
109 } else if ("col".equals(localName)) {
110 current.startCol(AttributeUtil.parseNonNegativeInteger(atts.getValue(
111 "", "span")));
112 } else if ("colgroup".equals(localName)) {
113 current.startColGroup(AttributeUtil.parseNonNegativeInteger(atts.getValue(
114 "", "span")));
115 }
116 }
117 }
118 }
119
120 /**
121 * @see org.whattf.checker.Checker#endElement(java.lang.String,
122 * java.lang.String, java.lang.String)
123 */
124 public void endElement(String uri, String localName, String qName)
125 throws SAXException {
126 if ("http://www.w3.org/1999/xhtml".equals(uri)) {
127 if ("table".equals(localName)) {
128 pop();
129 } else if (current != null) {
130 if ("td".equals(localName)) {
131 current.endCell();
132 } else if ("th".equals(localName)) {
133 current.endCell();
134 } else if ("tr".equals(localName)) {
135 current.endRow();
136 } else if ("tbody".equals(localName)
137 || "thead".equals(localName)
138 || "tfoot".equals(localName)) {
139 current.endRowGroup();
140 } else if ("col".equals(localName)) {
141 current.endCol();
142 } else if ("colgroup".equals(localName)) {
143 current.endColGroup();
144 }
145 }
146 }
147 }
148
149 /**
150 * @see org.whattf.checker.Checker#reset()
151 */
152 public void reset() {
153 stack.clear();
154 current = null;
155 }
156
157 }