import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Deque;
import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
+import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.dom.DOMSource;
import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema;
return this;
}
+ private static Map<QName, String> getElementAttributes(final XMLStreamReader in) {
+ Preconditions.checkState(in.isStartElement(), "Attributes can be extracted only from START_ELEMENT.");
+ final Map<QName, String> attributes = new LinkedHashMap<>();
+
+ for (int attrIndex = 0; attrIndex < in.getAttributeCount(); attrIndex++) {
+ String attributeNS = in.getAttributeNamespace(attrIndex);
+
+ if (attributeNS == null) {
+ attributeNS = "";
+ }
+
+ // Skip namespace definitions
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attributeNS)) {
+ continue;
+ }
+
+ final QName qName = new QName(URI.create(attributeNS), in.getAttributeLocalName(attrIndex));
+ attributes.put(qName, in.getAttributeValue(attrIndex));
+ }
+
+ return ImmutableMap.copyOf(attributes);
+ }
+
private static String readAnyXmlValue(final XMLStreamReader in) throws XMLStreamException {
final StringBuilder sb = new StringBuilder();
final String anyXmlElementName = in.getLocalName();
}
if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) {
+ parent.setAttributes(getElementAttributes(in));
setValue(parent, in.getElementText().trim(), in.getNamespaceContext());
if (isNextEndDocument(in)) {
return;
return;
}
+ if (parent instanceof ListEntryNodeDataWithSchema || parent instanceof ContainerNodeDataWithSchema) {
+ parent.setAttributes(getElementAttributes(in));
+ }
+
if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
String xmlElementName = in.getLocalName();
while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
return;
}
+ if (parent instanceof YangModeledAnyXmlSchemaNode) {
+ parent.setAttributes(getElementAttributes(in));
+ }
+
switch (in.nextTag()) {
case XMLStreamConstants.START_ELEMENT:
final Set<String> namesakes = new HashSet<>();
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.data.codec.xml;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class Bug8745Test {
+
+ @Test
+ public void testParsingAttributes() throws Exception {
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSource("/bug8745/foo.yang");
+ final QName contWithAttributes = QName.create("foo", "1970-01-01", "cont-with-attributes");
+ final ContainerSchemaNode contWithAttr = (ContainerSchemaNode) SchemaContextUtil.findDataSchemaNode(
+ schemaContext, SchemaPath.create(true, contWithAttributes));
+
+ final Document doc = loadDocument("/bug8745/foo.xml");
+
+ final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+
+ final XMLOutputFactory outputfactory = XMLOutputFactory.newInstance();
+ outputfactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
+
+ final XMLStreamWriter xmlStreamWriter = outputfactory.createXMLStreamWriter(domResult);
+
+ final NormalizedNodeStreamWriter streamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+ xmlStreamWriter, schemaContext);
+
+ final InputStream resourceAsStream = Bug8745Test.class.getResourceAsStream(
+ "/bug8745/foo.xml");
+ final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = inputFactory.createXMLStreamReader(resourceAsStream);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, contWithAttr);
+ xmlParser.parse(reader);
+
+ XMLUnit.setIgnoreWhitespace(true);
+ XMLUnit.setNormalize(true);
+
+ final String expectedXml = toString(doc.getDocumentElement());
+ final String serializedXml = toString(domResult.getNode());
+ final Diff diff = new Diff(expectedXml, serializedXml);
+
+ XMLAssert.assertXMLEqual(diff, true);
+ }
+
+ private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+ final InputStream resourceAsStream = NormalizedNodesToXmlTest.class.getResourceAsStream(xmlPath);
+ final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+ return currentConfigElement;
+ }
+
+ private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+ final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ private static String toString(final Node xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<cont-with-attributes xmlns="foo" xmlns:a="attribute-ns" a:operation="delete">
+ <leaf-with-attributes xmlns:a="attribute-ns" a:operation="create">lf-val</leaf-with-attributes>
+ <leaf-list-with-attributes xmlns:a="attribute-ns" a:operation="replace">lfl-val</leaf-list-with-attributes>
+ <list-with-attributes xmlns:a="attribute-ns" a:operation="merge">
+ <list-key>key-val</list-key>
+ </list-with-attributes>
+</cont-with-attributes>
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ container cont-with-attributes {
+ leaf leaf-with-attributes {
+ type string;
+ }
+
+ leaf-list leaf-list-with-attributes {
+ type string;
+ }
+
+ list list-with-attributes {
+ key list-key;
+
+ leaf list-key {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file
return new ImmutableNormalizedNodeStreamWriter(result);
}
+ protected Deque<NormalizedNodeContainerBuilder> getBuilders() {
+ return builders;
+ }
+
@SuppressWarnings("rawtypes")
- private NormalizedNodeContainerBuilder getCurrent() {
+ protected NormalizedNodeContainerBuilder getCurrent() {
return builders.peek();
}
}
@SuppressWarnings("rawtypes")
- private static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
+ protected static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
private final NormalizedNodeResult result;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import java.io.IOException;
+import java.util.Map;
import java.util.Objects;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@Beta
public abstract class AbstractNodeDataWithSchema {
private final DataSchemaNode schema;
+ private Map<QName, String> attributes;
public AbstractNodeDataWithSchema(final DataSchemaNode schema) {
this.schema = Preconditions.checkNotNull(schema);
return schema;
}
+ /**
+ * Set the associated attributes.
+ *
+ * @param attributes parsed attributes
+ */
+ public final void setAttributes(final Map<QName, String> attributes) {
+ Preconditions.checkState(this.attributes == null, "Node '%s' has already set its attributes to %s.",
+ getSchema().getQName(), this.attributes);
+ this.attributes = attributes;
+ }
+
+ /**
+ * Return the associated attributes.
+ *
+ * @return associated attributes
+ */
+ public final Map<QName, String> getAttributes() {
+ return attributes;
+ }
+
/**
* Emit this node's events into the specified writer.
*
package org.opendaylight.yangtools.yang.data.util;
import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@Override
public void write(final NormalizedNodeStreamWriter writer) throws IOException {
writer.nextDataSchemaNode(getSchema());
- writer.startContainerNode(provideNodeIdentifier(), childSizeHint());
+
+ if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(provideNodeIdentifier(), childSizeHint(),
+ getAttributes());
+ } else {
+ writer.startContainerNode(provideNodeIdentifier(), childSizeHint());
+ }
+
super.write(writer);
writer.endNode();
}
package org.opendaylight.yangtools.yang.data.util;
import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@Override
public void write(final NormalizedNodeStreamWriter writer) throws IOException {
writer.nextDataSchemaNode(getSchema());
- writer.leafSetEntryNode(getSchema().getQName(), getValue());
+
+ if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(getSchema().getQName(), getValue(),
+ getAttributes());
+ } else {
+ writer.leafSetEntryNode(getSchema().getQName(), getValue());
+ }
}
}
package org.opendaylight.yangtools.yang.data.util;
import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@Override
public void write(final NormalizedNodeStreamWriter writer) throws IOException {
writer.nextDataSchemaNode(getSchema());
- writer.leafNode(provideNodeIdentifier(), getValue());
+
+ if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafNode(provideNodeIdentifier(), getValue(),
+ getAttributes());
+ } else {
+ writer.leafNode(provideNodeIdentifier(), getValue());
+ }
}
}
import java.util.Map;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
}
writer.nextDataSchemaNode(getSchema());
- writer.startMapEntryNode(
- new NodeIdentifierWithPredicates(getSchema().getQName(), predicates),
- childSizeHint());
+
+ if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(
+ new NodeIdentifierWithPredicates(getSchema().getQName(), predicates), childSizeHint(),
+ getAttributes());
+ } else {
+ writer.startMapEntryNode(new NodeIdentifierWithPredicates(getSchema().getQName(), predicates),
+ childSizeHint());
+ }
+
super.write(writer);
writer.endNode();
}
package org.opendaylight.yangtools.yang.data.util;
import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode;
@Override
public void write(final NormalizedNodeStreamWriter writer) throws IOException {
writer.nextDataSchemaNode(getSchema());
- writer.startYangModeledAnyXmlNode(provideNodeIdentifier(), childSizeHint());
+
+ if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startYangModeledAnyXmlNode(provideNodeIdentifier(),
+ childSizeHint(), getAttributes());
+ } else {
+ writer.startYangModeledAnyXmlNode(provideNodeIdentifier(), childSizeHint());
+ }
+
super.write(writer);
writer.endNode();
}