nu.validator.checker.table.Cell Maven / Gradle / Ivy
Show all versions of validator Show documentation
/*
* Copyright (c) 2006 Henri Sivonen
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package nu.validator.checker.table;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* A table cell for table integrity checking.
*
* @version $Id$
* @author hsivonen
*/
final class Cell implements Locator {
// See
// http://mxr-test.landfill.bugzilla.org/mxr-test/seamonkey/source/content/html/content/src/nsHTMLTableCellElement.cpp
// for the source of these magic numbers.
/**
* Magic number from Gecko.
*/
private static final int MAX_COLSPAN = 1000;
/**
* Magic number from Gecko.
*/
private static final int MAX_ROWSPAN = 8190;
/**
* The column in which this cell starts. (Zero before positioning.)
*/
private int left;
/**
* The first row in the row group onto which this cell does not span.
* (rowspan before positioning)
*
* However, Integen.MAX_VALUE
is a magic value that means
* rowspan=0
.
*/
private int bottom;
/**
* The first column into which this cell does not span.
* (colspan before positioning.)
*/
private int right;
/**
* The value of the headers
attribute split on white space.
*/
private final String[] headers;
/**
* Whether this is a th
cell.
*/
private final boolean header;
/**
* Source column.
*/
private final int columnNumber;
/**
* Source line.
*/
private final int lineNumber;
/**
* Source public id.
*/
private final String publicId;
/**
* Source system id.
*/
private final String systemId;
/**
* The error handler.
*/
private final ErrorHandler errorHandler;
Cell(int colspan, int rowspan, String[] headers, boolean header,
Locator locator, ErrorHandler errorHandler) throws SAXException {
super();
this.errorHandler = errorHandler;
if (locator == null) {
this.columnNumber = -1;
this.lineNumber = -1;
this.publicId = null;
this.systemId = null;
} else {
this.columnNumber = locator.getColumnNumber();
this.lineNumber = locator.getLineNumber();
this.publicId = locator.getPublicId();
this.systemId = locator.getSystemId();
}
if (rowspan > MAX_ROWSPAN) {
warn("A rowspan attribute has the value " + rowspan
+ ", which exceeds the magic Gecko limit of " + MAX_ROWSPAN
+ ".");
}
if (colspan > MAX_COLSPAN) {
warn("A colspan attribute has the value " + colspan
+ ", which exceeds the magic browser limit of "
+ MAX_COLSPAN + ".");
}
if (rowspan == Integer.MAX_VALUE) {
throw new SAXException(
"Implementation limit reached. Table row counter overflowed.");
}
this.left = 0;
this.right = colspan;
this.bottom = (rowspan == 0 ? Integer.MAX_VALUE : rowspan);
this.headers = headers;
this.header = header;
}
/**
* Returns the headers.
*
* @return the headers
*/
public String[] getHeadings() {
return headers;
}
/**
* Returns the header.
*
* @return the header
*/
public boolean isHeader() {
return header;
}
public void warn(String message) throws SAXException {
if (errorHandler != null) {
errorHandler.warning(new SAXParseException(message, publicId,
systemId, lineNumber, columnNumber));
}
}
public void err(String message) throws SAXException {
if (errorHandler != null) {
errorHandler.error(new SAXParseException(message, publicId,
systemId, lineNumber, columnNumber));
}
}
/**
* Emit errors if this cell and the argument overlap horizontally.
* @param laterCell another cell
* @throws SAXException if the ErrorHandler
throws
*/
public void errOnHorizontalOverlap(Cell laterCell) throws SAXException {
if (!((laterCell.right <= left) || (right <= laterCell.left))) {
this.err("Table cell is overlapped by later table cell.");
laterCell.err("Table cell overlaps an earlier table cell.");
}
}
public void setPosition(int top, int left) throws SAXException {
this.left = left;
this.right += left;
if (this.right < 1) {
throw new SAXException(
"Implementation limit reached. Table column counter overflowed.");
}
if (this.bottom != Integer.MAX_VALUE) {
this.bottom += top;
if (this.bottom < 1) {
throw new SAXException(
"Implementation limit reached. Table row counter overflowed.");
}
}
}
public boolean shouldBeCulled(int row) {
return row >= bottom;
}
public int freeSlot(int potentialSlot) {
if (potentialSlot < left || potentialSlot >= right) {
return potentialSlot;
} else {
return right;
}
}
/**
* Returns the bottom.
*
* @return the bottom
*/
public int getBottom() {
return bottom;
}
/**
* Returns the left.
*
* @return the left
*/
int getLeft() {
return left;
}
/**
* Returns the right.
*
* @return the right
*/
int getRight() {
return right;
}
public void errIfNotRowspanZero(String rowGroupType) throws SAXException {
if (this.bottom != Integer.MAX_VALUE) {
err("Table cell spans past the end of its "
+ (rowGroupType == null ? "implicit row group"
: "row group established by a \u201C" + rowGroupType
+ "\u201D element")
+ "; clipped to the end of the row group.");
}
}
/**
* Returns the columnNumber.
*
* @return the columnNumber
*/
@Override
public int getColumnNumber() {
return columnNumber;
}
/**
* Returns the lineNumber.
*
* @return the lineNumber
*/
@Override
public int getLineNumber() {
return lineNumber;
}
/**
* Returns the publicId.
*
* @return the publicId
*/
@Override
public String getPublicId() {
return publicId;
}
/**
* Returns the systemId.
*
* @return the systemId
*/
@Override
public String getSystemId() {
return systemId;
}
public String elementName() {
return header ? "th" : "td";
}
}