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 fi.iki.hsivonen.xml.checker.table; 024 025 import java.util.LinkedList; 026 027 import org.xml.sax.Attributes; 028 import org.xml.sax.SAXException; 029 030 import fi.iki.hsivonen.xml.AttributeUtil; 031 import fi.iki.hsivonen.xml.checker.Checker; 032 033 /** 034 * Checks XHTML table integrity: overlapping cells, spanning past the end 035 * of row group, etc. 036 * 037 * @version $Id: TableChecker.java,v 1.5 2006/12/01 12:34:30 hsivonen Exp $ 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 fi.iki.hsivonen.xml.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 fi.iki.hsivonen.xml.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 fi.iki.hsivonen.xml.checker.Checker#reset() 151 */ 152 public void reset() { 153 stack.clear(); 154 current = null; 155 } 156 157 }