package com.occamlab.te.parsers; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * A SAX error handler that collects validation errors raised while verifying * the structure and content of XML entities. * */ public class XmlErrorHandler implements ErrorHandler { /** Storage for error messages. */ private StringBuffer buf = new StringBuffer(); /** Collection of reported validation errors. */ private List<ValidationError> errors = new ArrayList<ValidationError>(); private static Logger jlogger = Logger .getLogger("com.occamlab.te.parsers.XmlErrorHandler"); /** * Indicates whether any validation errors have been reported. * * @return true if any validation errors have been received. */ public boolean isEmpty() { return errors.isEmpty(); } /** * Receive notification of a warning. * * @param spex * a non-error condition reported by the parser * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */ public void warning(SAXParseException spex) { addError(ValidationError.WARNING, spex); } /** * Receive notification of a recoverable error. Typically this indicates * that a validation constraint has been violated. * * @param spex * a non-fatal error condition reported by the parser * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */ public void error(SAXParseException spex) { addError(ValidationError.ERROR, spex); } /** * Prints the error to STDOUT, used to be consistent with TEAM Engine error * handler. * */ void printError(String type, SAXParseException e) { PrintWriter logger = new PrintWriter(System.out); logger.print(type); if (e.getLineNumber() >= 0) { logger.print(" at line " + e.getLineNumber()); if (e.getColumnNumber() >= 0) { logger.print(", column " + e.getColumnNumber()); } if (e.getSystemId() != null) { logger.print(" of " + e.getSystemId()); } } else { if (e.getSystemId() != null) { logger.print(" in " + e.getSystemId()); } } logger.println(":"); logger.println(" " + e.getMessage()); logger.flush(); } /** * Receive notification of a non-recoverable error, such as a violation of * the well-formedness constraint. * * @param spex * a fatal error condition reported by the parser * @throws SAXException * if a fatal error (e.g., non-XML input, or ill-formed XML) * occurs while parsing the input * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */ public void fatalError(SAXParseException spex) throws SAXException { addError(ValidationError.FATAL_ERROR, spex); throw new SAXException("Fatal error while parsing input."); } /** * Adds a validation error based on a <code>SAXParseException</code>. * * @param severity * the severity of the error * @param spex * the <code>SAXParseException</code> raised while validating the * XML source */ private void addError(short severity, SAXParseException spex) { if (spex.getLineNumber() > 0) { buf.append("Line " + spex.getLineNumber() + " - "); } buf.append(spex.getMessage() + "\n"); ValidationError error = new ValidationError(severity, buf.toString()); errors.add(error); buf.setLength(0); } /** * Returns a concatenation of all received error messages. * * @return a consolidated error message */ public String toString() { buf.setLength(0); ErrorIterator errIterator = iterator(); while (errIterator.hasNext()) { ValidationError err = errIterator.next(); buf.append(err.getMessage()); } return buf.toString(); } /** * Returns a list of errors as strings. * * @return a list of error strings. */ public List<String> toList() { List<String> errorStrings = new ArrayList<String>(); ErrorIterator errIterator = iterator(); while (errIterator.hasNext()) { ValidationError err = errIterator.next(); errorStrings.add(err.getMessage()); } return errorStrings; } /** * Returns validation errors in a root container node. * The root container ({@code <errors>}) contains a list * of {@code <error>} elements containing error * messages as text content. * * @return Root element containing list of errors */ public Element toRootElement() { Document doc = null; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); doc = db.newDocument(); } catch (Exception e) { jlogger.log(Level.SEVERE, "validate", e); e.printStackTrace(); } Element root = doc.createElement("errors"); doc.appendChild(root); ErrorIterator errIterator = iterator(); while (errIterator.hasNext()) { ValidationError err = errIterator.next(); Element elem = doc.createElement("error"); elem.setTextContent(err.getMessage()); root.appendChild(elem); } return root; } /** * Returns validation errors in a NodeList (needed for CTL processing). Each * item in the list is an {@code <error>} element containing an error * message as text content. * * @return a list of errors in a NodeList. */ public NodeList toNodeList() { return toRootElement().getElementsByTagName("error"); } /** * Returns an iterator over the validation errors collected by this handler. * * @return a read-only <code>ErrorIterator</code> for this handler */ public ErrorIterator iterator() { return new ErrorIterator(); } /** * Clears all errors and messages. */ public void reset() { buf.setLength(0); errors.clear(); } /** * Helper class that provides a read-only iterator over validation errors. */ public class ErrorIterator { /** The underlying errors for this iterator. */ Iterator<ValidationError> underlying = errors.iterator(); /** * Indicates if more errors remain in the iteration. * * @return <code>true</code> if more errors remain. */ public boolean hasNext() { return underlying.hasNext(); } /** * Returns the next validation error in the iteration. * * @return the next error */ public ValidationError next() { return (ValidationError) underlying.next(); } } }