/* * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.rfc6020.repo; import com.google.common.base.Preconditions; import java.util.ArrayDeque; import java.util.Deque; import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.helpers.DefaultHandler; // adapted from https://stackoverflow.com/questions/4915422/get-line-number-from-xml-node-java final class StatementSourceReferenceHandler extends DefaultHandler { private static final Logger LOG = LoggerFactory.getLogger(StatementSourceReferenceHandler.class); private static final String USER_DATA_KEY = StatementSourceReference.class.getName(); private final Deque<Element> stack = new ArrayDeque<>(); private final StringBuilder sb = new StringBuilder(); private final Document doc; private final String file; private Locator locator; StatementSourceReferenceHandler(final Document doc, final String file) { this.doc = Preconditions.checkNotNull(doc); this.file = file; } static StatementSourceReference extractRef(final Element element) { final Object value = element.getUserData(USER_DATA_KEY); if (value instanceof StatementSourceReference) { return (StatementSourceReference) value; } if (value != null) { LOG.debug("Ignoring {} attached to key {}", value, USER_DATA_KEY); } return null; } @Override public void setDocumentLocator(final Locator locator) { // Save the locator, so that it can be used later for line tracking when traversing nodes. this.locator = locator; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) { addTextIfNeeded(); final Element el = doc.createElementNS(uri, qName); for (int i = 0, len = attributes.getLength(); i < len; i++) { el.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i)); } final StatementSourceReference ref = DeclarationInTextSource.atPosition(file, locator.getLineNumber(), locator.getColumnNumber()); el.setUserData(USER_DATA_KEY, ref, null); stack.push(el); } @Override public void endElement(final String uri, final String localName, final String qName) { addTextIfNeeded(); final Element closedEl = stack.pop(); Node parentEl = stack.peek(); if (parentEl == null) { // root element parentEl = doc; } parentEl.appendChild(closedEl); } @Override public void characters(final char ch[], final int start, final int length) throws SAXException { sb.append(ch, start, length); } // Outputs text accumulated under the current node private void addTextIfNeeded() { if (sb.length() > 0) { stack.peek().appendChild(doc.createTextNode(sb.toString())); sb.setLength(0); } } }