2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
10 import static java.util.Objects.requireNonNull;
12 import java.util.ArrayDeque;
13 import java.util.Deque;
14 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
15 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18 import org.w3c.dom.Document;
19 import org.w3c.dom.Element;
20 import org.w3c.dom.Node;
21 import org.xml.sax.Attributes;
22 import org.xml.sax.Locator;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.helpers.DefaultHandler;
26 // adapted from https://stackoverflow.com/questions/4915422/get-line-number-from-xml-node-java
27 final class StatementSourceReferenceHandler extends DefaultHandler {
28 private static final Logger LOG = LoggerFactory.getLogger(StatementSourceReferenceHandler.class);
29 private static final String USER_DATA_KEY = StatementSourceReference.class.getName();
31 private final Deque<Element> stack = new ArrayDeque<>();
32 private final StringBuilder sb = new StringBuilder();
33 private final Document doc;
34 private final String file;
36 private Locator documentLocator;
38 StatementSourceReferenceHandler(final Document doc, final String file) {
39 this.doc = requireNonNull(doc);
43 static StatementSourceReference extractRef(final Element element) {
44 final Object value = element.getUserData(USER_DATA_KEY);
45 if (value instanceof StatementSourceReference) {
46 return (StatementSourceReference) value;
49 LOG.debug("Ignoring {} attached to key {}", value, USER_DATA_KEY);
55 public void setDocumentLocator(final Locator locator) {
56 // Save the locator, so that it can be used later for line tracking when traversing nodes.
57 this.documentLocator = locator;
61 @SuppressWarnings("checkstyle:parameterName")
62 public void startElement(final String uri, final String localName, final String qName,
63 final Attributes attributes) {
65 final Element el = doc.createElementNS(uri, qName);
66 for (int i = 0, len = attributes.getLength(); i < len; i++) {
67 el.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i));
70 final StatementSourceReference ref = DeclarationInTextSource.atPosition(file, documentLocator.getLineNumber(),
71 documentLocator.getColumnNumber());
72 el.setUserData(USER_DATA_KEY, ref, null);
77 @SuppressWarnings("checkstyle:parameterName")
78 public void endElement(final String uri, final String localName, final String qName) {
80 final Element closedEl = stack.pop();
81 Node parentEl = stack.peek();
82 if (parentEl == null) {
87 parentEl.appendChild(closedEl);
91 public void characters(final char[] ch, final int start, final int length) throws SAXException {
92 sb.append(ch, start, length);
95 // Outputs text accumulated under the current node
96 private void addTextIfNeeded() {
97 if (sb.length() > 0) {
98 stack.peek().appendChild(doc.createTextNode(sb.toString()));