<version>3.0.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-data-api</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-data-util</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>rfc7952-model-api</artifactId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-data-util</artifactId>
+ </dependency>
</dependencies>
</project>
<module>rfc6536-parser-support</module>
<!-- Metadata (annotation) metamodel support -->
+ <module>rfc7952-data-api</module>
+ <module>rfc7952-data-util</module>
<module>rfc7952-model-api</module>
<module>rfc7952-parser-support</module>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>../../bundle-parent</relativePath>
+ </parent>
+
+ <artifactId>rfc7952-data-api</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+ <description>RFC7952 data model extensions</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-model-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-maven-plugin</artifactId>
+ <configuration>
+ <failOnError>true</failOnError>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+/**
+ * RFC7952 metadata counterpart to a {@link NormalizedNode}. This interface is meant to be used as a companion to
+ * a NormalizedNode instance, hence it does not support iterating over its structure like it is possible with
+ * {@link NormalizedNode#getValue()}.
+ *
+ * <p>
+ * This model of metadata <em>does not</em> have the RFC7952 restriction on metadata attachment to {@code list}s and
+ * {@code leaf-list}s because NormalizedNode data model has {@link LeafSetNode}, {@link MapNode} and
+ * {@link UnkeyedListNode} to which metadata can be attached.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NormalizedMetadata extends Identifiable<PathArgument>, Immutable {
+ /**
+ * Return the set of annotations defined in this metadata node. Values are expected to be effectively-immutable
+ * scalar types, like {@link String}s, {@link Number}s and similar.
+ *
+ * @return The set of annotations attached to the corresponding data node.
+ */
+ @NonNull Map<QName, Object> getAnnotations();
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+/**
+ * RFC7952 metadata counterpart to a {@link NormalizedNodeContainer}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NormalizedMetadataContainer extends NormalizedMetadata {
+ /**
+ * Returns child node identified by provided key.
+ *
+ * @param child Path argument identifying child node
+ * @return Optional with child node if child exists, {@link Optional#empty()} if it does not.
+ */
+ Optional<? extends NormalizedMetadata> getChild(PathArgument child);
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+
+/**
+ * Extension to the NormalizedNodeStreamWriter with metadata support. Semantically this extends the event model of
+ * {@link NormalizedNodeStreamWriter} with two new events:
+ * <ul>
+ * <li>{@link #startMetadata(int)} is within the scope of any open node and starts a block of metadata entries. It
+ * is recommended to emit this block before any other events. Only
+ * {@link #startMetadataEntry(QName, AnnotationSchemaNode)} and {@link NormalizedNodeStreamWriter#endNode()} are valid
+ * in this node. This event may be emitted at most once for any open node.
+ * </li>
+ * <li>{@link #startMetadataEntry(QName, AnnotationSchemaNode)} to start a metadata entry. Its value is must be emitted
+ * via {@link NormalizedNodeStreamWriter#nodeValue(Object)} before the entry is closed via
+ * {@link NormalizedNodeStreamWriter#endNode()}.
+ * </ul>
+ *
+ * <p>
+ * Note that some implementations of this interface, notably those targeting streaming XML, may require metadata to
+ * be emitted before any other events. Such requirement is communicated through {@link #requireMetadataFirst()} and
+ * users must honor it. If such requirement is not set, metadata may be emitted at any time.
+ *
+ * <p>
+ * Furthermore implementations targeting RFC7952 encoding towards external systems are required to handle metadata
+ * attached to {@code leaf-list} and {@code list} nodes by correctly extending them to each entry.
+ */
+@Beta
+public interface NormalizedMetadataStreamWriter extends NormalizedNodeStreamWriterExtension {
+ /**
+ * Start the metadata block for the currently-open node. Allowed events are
+ * {@link #startMetadataEntry(QName, AnnotationSchemaNode)} and {@link NormalizedNodeStreamWriter#endNode()}.
+ *
+ * @param childSizeHint Non-negative count of expected direct child nodes or
+ * {@link NormalizedNodeStreamWriter#UNKNOWN_SIZE} if count is unknown. This is only hint and
+ * should not fail writing of child events, if there are more events than count.
+ * @throws IllegalStateException if current node already has a metadata block or cannot receive metadata -- for
+ * example because {@link #requireMetadataFirst()} was not honored.
+ * @throws IOException if an underlying IO error occurs
+ */
+ void startMetadata(int childSizeHint) throws IOException;
+
+ /**
+ * Start a new metadata entry. The value of the metadata entry should be emitted through
+ * {@link NormalizedNodeStreamWriter#nodeValue(Object)}.
+ *
+ * @param name Metadata name, as defined through {@code md:annotation}
+ * @param schema Effective {@code md:annotation} schema, or null if unknown to the caller
+ * @throws NullPointerException if {@code name} is null
+ * @throws IllegalStateException when this method is invoked outside of a metadata block
+ * @throws IOException if an underlying IO error occurs
+ */
+ void startMetadataEntry(@NonNull QName name, @Nullable AnnotationSchemaNode schema) throws IOException;
+
+ /**
+ * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
+ * returns false.
+ *
+ * @return True if metadata must occur just after the start of an entry.
+ */
+ default boolean requireMetadataFirst() {
+ return false;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>../../bundle-parent</relativePath>
+ </parent>
+
+ <artifactId>rfc7952-data-util</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+ <description>RFC7952 data model utilities</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc7952-model-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-maven-plugin</artifactId>
+ <configuration>
+ <failOnError>true</failOnError>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, 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.rfc7952.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+
+/**
+ * A utility class to attach {@link NormalizedMetadata} into a NormalizedNode stream, such as the one produced by
+ * {@link NormalizedNodeWriter}, so that a target {@link NormalizedNodeStreamWriter} sees both data and metadata in
+ * the stream. A typical use would like this:
+ *
+ * <p>
+ * <code>
+ * // Data for output
+ * NormalizedNode<?, ?> data;
+ * // Metadata for output
+ * NormalizedMetadata metadata;
+ *
+ * // Target output writer
+ * NormalizedNodeStreamWriter output = ...;
+ * // Metadata writer
+ * NormalizedMetadataStreamWriter metaWriter = NormalizedMetadataWriter.forStreamWriter(output);
+ *
+ * // Write a normalized node and its metadata
+ * dataWriter.write(data, metadata);
+ * </code>
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NotThreadSafe
+public final class NormalizedMetadataWriter implements Closeable, Flushable {
+ private final NormalizedNodeStreamWriter writer;
+ private final boolean orderKeyLeaves;
+
+ private NormalizedMetadataWriter(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) {
+ this.writer = requireNonNull(writer);
+ this.orderKeyLeaves = orderKeyLeaves;
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
+ * {@link #forStreamWriter(NormalizedNodeStreamWriter)} method, this allows the caller to switch off RFC6020 XML
+ * compliance, providing better throughput. The reason is that the XML mapping rules in RFC6020 require
+ * the encoding to emit leaf nodes which participate in a list's key first and in the order in which they are
+ * defined in the key. For JSON, this requirement is completely relaxed and leaves can be ordered in any way we
+ * see fit. The former requires a bit of work: first a lookup for each key and then for each emitted node we need
+ * to check whether it was already emitted.
+ *
+ * @param writer Back-end writer
+ * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+ * @return A new instance.
+ */
+ public static @NonNull NormalizedMetadataWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
+ final boolean orderKeyLeaves) {
+ return new NormalizedMetadataWriter(writer, orderKeyLeaves);
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. This is a convenience method for
+ * {@code forStreamWriter(writer, true)}.
+ *
+ * @param writer Back-end writer
+ * @return A new instance.
+ */
+ public static @NonNull NormalizedMetadataWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+ return forStreamWriter(writer, true);
+ }
+
+ /**
+ * Iterate over the provided {@link NormalizedNode} and {@link NormalizedMetadata} and emit write events to the
+ * encapsulated {@link NormalizedNodeStreamWriter}.
+ *
+ * @param data NormalizedNode data
+ * @param metadata {@link NormalizedMetadata} metadata
+ * @return NormalizedNodeWriter this
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if metadata does not match data
+ * @throws IOException when thrown from the backing writer.
+ */
+ public @NonNull NormalizedMetadataWriter write(final NormalizedNode<?, ?> data, final NormalizedMetadata metadata)
+ throws IOException {
+ final PathArgument dataId = data.getIdentifier();
+ final PathArgument metaId = metadata.getIdentifier();
+ checkArgument(dataId.equals(metaId), "Mismatched data %s and metadata %s", dataId, metaId);
+
+ final NormalizedMetadataStreamWriter metaWriter = writer.getExtensions()
+ .getInstance(NormalizedMetadataStreamWriter.class);
+ final NormalizedNodeStreamWriter delegate = metaWriter == null ? writer
+ : new NormalizedNodeStreamWriterMetadataDecorator(writer, metaWriter, metadata);
+
+ final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(delegate, orderKeyLeaves);
+ nnWriter.write(data);
+ nnWriter.flush();
+ return this;
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, 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.rfc7952.data.util;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataContainer;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * A simple decorator on top of a NormalizedNodeStreamWriter, which attaches NormalizedMetadata to the event stream,
+ * so that the metadata is emitted along with data.
+ */
+final class NormalizedNodeStreamWriterMetadataDecorator extends ForwardingNormalizedNodeStreamWriter {
+ private final Deque<NormalizedMetadata> stack = new ArrayDeque<>();
+ private final NormalizedMetadataStreamWriter metaWriter;
+ private final NormalizedNodeStreamWriter writer;
+ private final NormalizedMetadata metadata;
+
+ NormalizedNodeStreamWriterMetadataDecorator(final NormalizedNodeStreamWriter writer,
+ final NormalizedMetadataStreamWriter metaWriter, final NormalizedMetadata metadata) {
+ this.writer = requireNonNull(writer);
+ this.metaWriter = requireNonNull(metaWriter);
+ this.metadata = requireNonNull(metadata);
+ }
+
+ @Override
+ protected NormalizedNodeStreamWriter delegate() {
+ return writer;
+ }
+
+ @Override
+ public void startLeafNode(final NodeIdentifier name) throws IOException {
+ super.startLeafNode(name);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startLeafSet(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startOrderedLeafSet(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+ super.startLeafSetEntryNode(name);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startContainerNode(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startUnkeyedList(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startUnkeyedListItem(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startMapNode(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IOException {
+ super.startMapEntryNode(identifier, childSizeHint);
+ enterMetadataNode(identifier);
+ }
+
+ @Override
+ public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startOrderedMapNode(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startChoiceNode(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+ super.startAugmentationNode(identifier);
+ enterMetadataNode(identifier);
+ }
+
+ @Override
+ public void startAnyxmlNode(final NodeIdentifier name) throws IOException {
+ super.startAnyxmlNode(name);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ super.startYangModeledAnyXmlNode(name, childSizeHint);
+ enterMetadataNode(name);
+ }
+
+ @Override
+ public void endNode() throws IOException {
+ super.endNode();
+ stack.pop();
+ }
+
+ private void enterMetadataNode(final PathArgument name) throws IOException {
+ final NormalizedMetadata child = findMetadata(name);
+ if (child != null) {
+ emitAnnotations(child.getAnnotations());
+ }
+ stack.push(child);
+ }
+
+ private @Nullable NormalizedMetadata findMetadata(final PathArgument name) {
+ final NormalizedMetadata current = stack.peek();
+ if (current instanceof NormalizedMetadataContainer) {
+ return ((NormalizedMetadataContainer) current).getChild(name).orElse(null);
+ }
+
+ // This may either be the first entry or unattached metadata nesting
+ return stack.isEmpty() ? metadata : null;
+ }
+
+ private void emitAnnotations(final Map<QName, Object> annotations) throws IOException {
+ if (!annotations.isEmpty()) {
+ metaWriter.startMetadata(annotations.size());
+ for (Entry<QName, Object> entry : annotations.entrySet()) {
+ metaWriter.startMetadataEntry(entry.getKey(), null);
+ writer.nodeValue(entry.getValue());
+ writer.endNode();
+ }
+ writer.endNode();
+ }
+ }
+}