import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaResolver;
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;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
/**
* A {@link NormalizedNodeStreamWriterExtension} exposed by stream writers which can handle mount point data, notably
* @throws NullPointerException if label is null
*/
Optional<MountPointSchemaResolver> findMountPoint(@NonNull QName label);
+
+ /**
+ * Start a new mount point with a specific root context.
+ *
+ * @param label Mount point label
+ * @param mountContext SchemaContext associated with the context
+ * @return A new NormalizedNodeStreamWriter, or empty if the mount point data should be ignored
+ */
+ Optional<NormalizedNodeStreamWriter> startMountPoint(@NonNull QName label, @NonNull SchemaContext mountContext);
}
@Beta
public interface StaticMountPointSchemaResolver extends MountPointSchemaResolver, SchemaContextProvider {
@Override
+ // FIXME: remove this override when SchemaContextProvider's method has sane semantics.
@NonNull SchemaContext getSchemaContext();
}
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import java.net.URI;
+import java.util.Arrays;
+import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
public static final String MODULE_NAME = "ietf-yang-library";
/**
- * Top-level containers which hold YANG Library information.
+ * Top-level containers which hold YANG Library information, ordered by descending preference, with more modern
+ * and/or preferred entries first.
*/
public enum ContainerName {
+ // Note: order this enum from most-preferred to least-preferred name
/**
- * Container in RFC7895 (pre-NMDA) YANG Library.
+ * Container in RFC8525 (NMDA) YANG Library.
*/
- RFC7895("modules-state"),
+ RFC8525("yang-library"),
/**
- * Container in RFC8525 (NMDA) YANG Library.
+ * Container in RFC7895 (pre-NMDA) YANG Library.
*/
- RFC8525("yang-library");
+ RFC7895("modules-state");
+
+ private static final ImmutableMap<String, ContainerName> NAME_TO_ENUM = Maps.uniqueIndex(
+ Arrays.asList(values()), ContainerName::getLocalName);
private final String localName;
public String getLocalName() {
return localName;
}
+
+ public static Optional<ContainerName> forLocalName(final String localName) {
+ return Optional.ofNullable(NAME_TO_ENUM.get(requireNonNull(localName)));
+ }
}
private YangLibraryConstants() {
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>rfc7952-data-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc8528-data-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-api</artifactId>
--- /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.yang.data.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * A {@link CompositeNodeDataWithSchema} which can hold mount-point data. This data is manipulated through
+ * {@link #getMountPointData(QName)}.
+ */
+@Beta
+public abstract class AbstractMountPointDataWithSchema<T extends DataSchemaNode>
+ extends CompositeNodeDataWithSchema<T> {
+ private MountPointData mountedData;
+
+ AbstractMountPointDataWithSchema(final T schema) {
+ super(schema);
+ }
+
+ @Override
+ public void write(final NormalizedNodeStreamWriter writer, final NormalizedMetadataStreamWriter metaWriter)
+ throws IOException {
+ super.write(writer, metaWriter);
+ if (mountedData != null) {
+ mountedData.write(writer);
+ }
+ }
+
+ public final MountPointData getMountPointData(final QName label) {
+ if (mountedData != null) {
+ final QName existing = mountedData.getIdentifier();
+ checkState(label.equals(existing), "Mismatched mount label {}, already have {}", label, existing);
+ } else {
+ mountedData = new MountPointData(label);
+ }
+ return mountedData;
+ }
+}
* <p>
* Represents a YANG container node.
*/
-public class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema<ContainerSchemaNode> {
+public class ContainerNodeDataWithSchema extends AbstractMountPointDataWithSchema<ContainerSchemaNode> {
public ContainerNodeDataWithSchema(final ContainerSchemaNode schema) {
super(schema);
* <p>
* Represents a YANG list entry node.
*/
-public abstract class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema<ListSchemaNode> {
+public abstract class ListEntryNodeDataWithSchema extends AbstractMountPointDataWithSchema<ListSchemaNode> {
private static final class Keyed extends ListEntryNodeDataWithSchema {
private final Map<QName, SimpleNodeDataWithSchema<?>> keyValues = new HashMap<>();
// This template results in Maps in schema definition order
--- /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.yang.data.util;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizableAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A raw child of {@link MountPointData}. This is similar in functionality to {@link NormalizableAnydata}, but
+ * rather than normalizing, the data is fed into a combination of a SchemaContext and NormalizedNodeStreamWriter.
+ */
+@Beta
+@NonNullByDefault
+public interface MountPointChild {
+ /**
+ * Stream this child into a writer, with the help of a SchemaContext.
+ *
+ * @param writer Writer to emit the child into
+ * @param schemaContext SchemaContext for normalization purposes
+ * @throws IOException if an underlying error occurs
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void writeTo(NormalizedNodeStreamWriter writer, SchemaContext schemaContext) throws IOException;
+
+ /**
+ * Normalized this child to a particular SchemaContext.
+ *
+ * @param schemaContext SchemaContext for normalization purposes
+ * @return A NormalizedNode representation of this child
+ * @throws IOException if an underlying error occurs
+ * @throws NullPointerException if any of the arguments is null
+ */
+ NormalizedNode<?, ?> normalizeTo(SchemaContext schemaContext) throws IOException;
+}
--- /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.yang.data.util;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.rfc8528.data.api.DynamicMountPointSchemaResolver;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointStreamWriter;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaResolver;
+import org.opendaylight.yangtools.rfc8528.model.api.StaticMountPointSchemaResolver;
+import org.opendaylight.yangtools.rfc8528.model.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * YANG Schema Mount-supported data attached to either a {@code list} item or a {@code container}.
+ */
+@Beta
+public final class MountPointData extends AbstractIdentifiable<QName> {
+ private static final Logger LOG = LoggerFactory.getLogger(MountPointData.class);
+
+ private final Map<ContainerName, MountPointChild> yangLib = new EnumMap<>(ContainerName.class);
+ private final List<MountPointChild> children = new ArrayList<>();
+
+ MountPointData(final QName label) {
+ super(label);
+ }
+
+ public void setContainer(final @NonNull ContainerName containerName, final @NonNull MountPointChild data) {
+ final MountPointChild prev = yangLib.putIfAbsent(containerName, requireNonNull(data));
+ checkState(prev == null, "Attempted to duplicate container %s data %s with %s", containerName, prev, data);
+ addChild(data);
+ }
+
+ public void addChild(final @NonNull MountPointChild data) {
+ children.add(requireNonNull(data));
+ }
+
+ void write(final @NonNull NormalizedNodeStreamWriter writer) throws IOException {
+ final MountPointStreamWriter mountWriter = writer.getExtensions().getInstance(MountPointStreamWriter.class);
+ if (mountWriter == null) {
+ LOG.debug("Writer {} does not support mount points, ignoring data in {}", writer, getIdentifier());
+ return;
+ }
+
+ final Optional<MountPointSchemaResolver> optResolver = mountWriter.findMountPoint(getIdentifier());
+ if (!optResolver.isPresent()) {
+ LOG.debug("Mount point for {} is not present, ignoring it", getIdentifier());
+ return;
+ }
+
+ final MountPointSchemaResolver resolver = optResolver.get();
+ if (resolver instanceof StaticMountPointSchemaResolver) {
+ writeStatic(mountWriter, ((StaticMountPointSchemaResolver) resolver).getSchemaContext());
+ } else if (resolver instanceof DynamicMountPointSchemaResolver) {
+ writeDynamic(mountWriter, (DynamicMountPointSchemaResolver) resolver);
+ } else {
+ throw new IOException("Unhandled resolver " + resolver);
+ }
+ }
+
+ private void writeDynamic(final @NonNull MountPointStreamWriter mountWriter,
+ final DynamicMountPointSchemaResolver resolver) throws IOException {
+ for (Entry<ContainerName, MountPointChild> entry : yangLib.entrySet()) {
+ final Optional<SchemaContext> optContext = resolver.findContainerContext(entry.getKey());
+ if (!optContext.isPresent()) {
+ LOG.debug("YANG Library context for mount point {} container {} not found", getIdentifier(),
+ entry.getKey());
+ continue;
+ }
+
+ final NormalizedNode<?, ?> data = entry.getValue().normalizeTo(optContext.get());
+ if (!(data instanceof ContainerNode)) {
+ throw new IOException("Invalid non-container " + data);
+ }
+
+ final SchemaContext context;
+ try {
+ context = resolver.assembleSchemaContext((ContainerNode) data);
+ } catch (YangParserException e) {
+ throw new IOException("Failed to assemble context for " + data, e);
+ }
+
+ writeStatic(mountWriter, context);
+ return;
+ }
+
+ LOG.warn("Failed to create a dynamic context for mount point {}, ignoring its data", getIdentifier());
+ }
+
+ private void writeStatic(final @NonNull MountPointStreamWriter mountWriter,
+ final @NonNull SchemaContext schemaContext) throws IOException {
+ final Optional<NormalizedNodeStreamWriter> optWriter = mountWriter.startMountPoint(getIdentifier(),
+ schemaContext);
+ if (!optWriter.isPresent()) {
+ LOG.debug("Ignoring mount point {} data due to writer decision", getIdentifier());
+ return;
+ }
+
+ try (NormalizedNodeStreamWriter writer = optWriter.get()) {
+ for (MountPointChild child : children) {
+ child.writeTo(writer, schemaContext);
+ }
+ }
+ }
+}