package net.jangaroo.jooc.util; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.DefaultHandler2; import java.util.Stack; /** * SAX Handler that fills a DOM document with the XML nodes and * ads line numbers to the element user data */ public class PreserveLineNumberHandler extends DefaultHandler2 { private static final String LINE_NUMBER_KEY_NAME = "lineNumber"; private static final String COLUMN_NUMBER_KEY_NAME = "columnNumber"; private final Stack<Node> elementStack = new Stack<Node>(); private final StringBuilder textBuffer = new StringBuilder(); private Document doc; private Locator locator; public PreserveLineNumberHandler(Document doc) { this.doc = doc; elementStack.push(doc); } public static int getLineNumber(Node node) { String lineStr = (String) node.getUserData(PreserveLineNumberHandler.LINE_NUMBER_KEY_NAME); return lineStr == null ? -1 : Integer.parseInt(lineStr); } public static int getColumnNumber(Node node) { String lineStr = (String) node.getUserData(PreserveLineNumberHandler.COLUMN_NUMBER_KEY_NAME); return lineStr == null ? -1 : Integer.parseInt(lineStr); } @Override public void setDocumentLocator(final Locator locator) { this.locator = locator; // Save the locator, so that it can be used later for line tracking when traversing nodes. } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { addTextIfNeeded(); final Element el = doc.createElementNS(uri, qName); for (int i = 0; i < attributes.getLength(); i++) { el.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i)); } el.setUserData(LINE_NUMBER_KEY_NAME, String.valueOf(this.locator.getLineNumber()), null); el.setUserData(COLUMN_NUMBER_KEY_NAME, String.valueOf(this.locator.getColumnNumber()), null); elementStack.push(el); } @Override public void endElement(final String uri, final String localName, final String qName) { addTextIfNeeded(); final Node closedEl = elementStack.pop(); elementStack.peek().appendChild(closedEl); } @Override public void characters(final char ch[], final int start, final int length) throws SAXException { textBuffer.append(ch, start, length); } @Override public void comment(char[] ch, int start, int length) throws SAXException { super.comment(ch, start, length); Comment comment = doc.createComment(new String(ch, start, length)); final Node parentEl = elementStack.peek(); parentEl.appendChild(comment); } // Outputs text accumulated under the current node private void addTextIfNeeded() { if (textBuffer.length() > 0) { final Node textNode = doc.createTextNode(textBuffer.toString()); elementStack.peek().appendChild(textNode); textBuffer.delete(0, textBuffer.length()); } } }