--- /dev/null
+package org.opendaylight.controller.sal.rest.api;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface RestconfNormalizedNodeWriter extends Flushable, Closeable {
+
+ RestconfNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.controller.sal.rest.impl;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an experimental iterator over a {@link NormalizedNode}. This is essentially
+ * the opposite of a {@link XMLStreamReader} -- unlike instantiating an iterator over
+ * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
+ * us to write multiple nodes.
+ */
+@Beta
+public class DepthAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
+ private final NormalizedNodeStreamWriter writer;
+ protected int currentDepth = 0;
+ protected final int maxDepth;
+
+ private DepthAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final int maxDepth) {
+ this.writer = Preconditions.checkNotNull(writer);
+ this.maxDepth = maxDepth;
+ }
+
+ protected final NormalizedNodeStreamWriter getWriter() {
+ return writer;
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
+ *
+ * @param writer Back-end writer
+ * @return A new instance.
+ */
+ public static DepthAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, final int maxDepth) {
+ return forStreamWriter(writer, true, maxDepth);
+ }
+
+ /**
+ * 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 DepthAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, final boolean
+ orderKeyLeaves, final int maxDepth) {
+ if (orderKeyLeaves) {
+ return new OrderedDepthAwareNormalizedNodeWriter(writer, maxDepth);
+ } else {
+ return new DepthAwareNormalizedNodeWriter(writer, maxDepth);
+ }
+ }
+
+ /**
+ * Iterate over the provided {@link NormalizedNode} and emit write
+ * events to the encapsulated {@link NormalizedNodeStreamWriter}.
+ *
+ * @param node Node
+ * @return
+ * @throws IOException when thrown from the backing writer.
+ */
+ public final DepthAwareNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ if (wasProcessedAsCompositeNode(node)) {
+ return this;
+ }
+
+ if (wasProcessAsSimpleNode(node)) {
+ return this;
+ }
+
+ throw new IllegalStateException("It wasn't possible to serialize node " + node);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * Emit a best guess of a hint for a particular set of children. It evaluates the
+ * iterable to see if the size can be easily gotten to. If it is, we hint at the
+ * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+ *
+ * @param children Child nodes
+ * @return Best estimate of the collection size required to hold all the children.
+ */
+ static final int childSizeHint(final Iterable<?> children) {
+ return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+ }
+
+ private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+ if (node instanceof LeafSetEntryNode) {
+ if (currentDepth < maxDepth) {
+ final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>) node;
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
+ } else {
+ writer.leafSetEntryNode(nodeAsLeafList.getValue());
+ }
+ }
+ return true;
+ } else if (node instanceof LeafNode) {
+ final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+ if(writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
+ } else {
+ writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+ }
+ return true;
+ } else if (node instanceof AnyXmlNode) {
+ final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+ writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Emit events for all children and then emit an endNode() event.
+ *
+ * @param children Child iterable
+ * @return True
+ * @throws IOException when the writer reports it
+ */
+ protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
+ if (currentDepth < maxDepth) {
+ for (NormalizedNode<?, ?> child : children) {
+ write(child);
+ }
+ }
+ writer.endNode();
+ return true;
+ }
+
+ protected boolean writeMapEntryChildren(final MapEntryNode mapEntryNode) throws IOException {
+ if (currentDepth < maxDepth) {
+ writeChildren(mapEntryNode.getValue());
+ } else if (currentDepth == maxDepth) {
+ writeOnlyKeys(mapEntryNode.getIdentifier().getKeyValues());
+ }
+ return true;
+ }
+
+ private void writeOnlyKeys(Map<QName, Object> keyValues) throws IllegalArgumentException, IOException {
+ for (Map.Entry<QName, Object> entry : keyValues.entrySet()) {
+ writer.leafNode(new NodeIdentifier(entry.getKey()), entry.getValue());
+ }
+ writer.endNode();
+
+ }
+
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ if(writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer)
+ .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+ } else {
+ writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+ }
+ currentDepth++;
+ writeMapEntryChildren(node);
+ currentDepth--;
+ return true;
+ }
+
+ private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+ boolean processedAsCompositeNode = false;
+ if (node instanceof ContainerNode) {
+ final ContainerNode n = (ContainerNode) node;
+ if(writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes());
+ } else {
+ writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ }
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue());
+ currentDepth--;
+ }
+ else if (node instanceof MapEntryNode) {
+ processedAsCompositeNode = writeMapEntryNode((MapEntryNode) node);
+ }
+ else if (node instanceof UnkeyedListEntryNode) {
+ final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+ writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue());
+ currentDepth--;
+ }
+ else if (node instanceof ChoiceNode) {
+ final ChoiceNode n = (ChoiceNode) node;
+ writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue());
+ }
+ else if (node instanceof AugmentationNode) {
+ final AugmentationNode n = (AugmentationNode) node;
+ writer.startAugmentationNode(n.getIdentifier());
+ processedAsCompositeNode = writeChildren(n.getValue());
+ }
+ else if (node instanceof UnkeyedListNode) {
+ final UnkeyedListNode n = (UnkeyedListNode) node;
+ writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue());
+ }
+ else if (node instanceof OrderedMapNode) {
+ final OrderedMapNode n = (OrderedMapNode) node;
+ writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue());
+ }
+ else if (node instanceof MapNode) {
+ final MapNode n = (MapNode) node;
+ writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue());
+ }
+ else if (node instanceof LeafSetNode) {
+ //covers also OrderedLeafSetNode for which doesn't exist start* method
+ final LeafSetNode<?> n = (LeafSetNode<?>) node;
+ writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue());
+ currentDepth--;
+ }
+
+ return processedAsCompositeNode;
+ }
+
+ private static final class OrderedDepthAwareNormalizedNodeWriter extends DepthAwareNormalizedNodeWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(OrderedDepthAwareNormalizedNodeWriter.class);
+
+ OrderedDepthAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final int maxDepth) {
+ super(writer, maxDepth);
+ }
+
+ @Override
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ final NormalizedNodeStreamWriter writer = getWriter();
+ if(writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+ } else {
+ writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+ }
+
+ final Set<QName> qnames = node.getIdentifier().getKeyValues().keySet();
+ // Write out all the key children
+ for (QName qname : qnames) {
+ final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+ if (child.isPresent()) {
+ write(child.get());
+ } else {
+ LOG.info("No child for key element {} found", qname);
+ }
+ }
+
+ // Write all the rest
+ currentDepth++;
+ boolean result = writeChildren(Iterables.filter(node.getValue(), new Predicate<NormalizedNode<?, ?>>() {
+ @Override
+ public boolean apply(final NormalizedNode<?, ?> input) {
+ if (input instanceof AugmentationNode) {
+ return true;
+ }
+ if (!qnames.contains(input.getNodeType())) {
+ return true;
+ }
+
+ LOG.debug("Skipping key child {}", input);
+ return false;
+ }
+ }));
+ currentDepth--;
+ return result;
+ }
+ }
+}
package org.opendaylight.controller.sal.rest.impl;
import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.OutputStream;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.opendaylight.controller.sal.rest.api.Draft02;
+import org.opendaylight.controller.sal.rest.api.RestconfNormalizedNodeWriter;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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;
import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
SchemaPath path = context.getSchemaNode().getPath();
final JsonWriter jsonWriter = createJsonWriter(entityStream, t.getWriterParameters().isPrettyPrint());
jsonWriter.beginObject();
- writeNormalizedNode(jsonWriter,path,context,data);
+ writeNormalizedNode(jsonWriter,path,context,data, t.getWriterParameters().getDepth());
jsonWriter.endObject();
jsonWriter.flush();
}
private void writeNormalizedNode(JsonWriter jsonWriter, SchemaPath path,
- InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data) throws IOException {
- final NormalizedNodeWriter nnWriter;
+ InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data, Optional<Integer> depth) throws
+ IOException {
+ final RestconfNormalizedNodeWriter nnWriter;
if (SchemaPath.ROOT.equals(path)) {
/*
* Creates writer without initialNs and we write children of root data container
* which is not visible in restconf
*/
- nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter, depth);
writeChildren(nnWriter,(ContainerNode) data);
} else if (context.getSchemaNode() instanceof RpcDefinition) {
/*
* so we need to emit initial output declaratation..
*/
path = ((RpcDefinition) context.getSchemaNode()).getOutput().getPath();
- nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter, depth);
jsonWriter.name("output");
jsonWriter.beginObject();
writeChildren(nnWriter, (ContainerNode) data);
if(data instanceof MapEntryNode) {
data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).withChild(((MapEntryNode) data)).build();
}
- nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter, depth);
nnWriter.write(data);
}
nnWriter.flush();
}
- private void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
+ private void writeChildren(final RestconfNormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
for(final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
nnWriter.write(child);
}
}
- private NormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
- final SchemaPath path, final JsonWriter jsonWriter) {
+ private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
+ final SchemaPath path, final JsonWriter jsonWriter, Optional<Integer> depth) {
final SchemaNode schema = context.getSchemaNode();
final JSONCodecFactory codecs = getCodecFactory(context);
initialNs = null;
}
final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(codecs,path,initialNs,jsonWriter);
- return NormalizedNodeWriter.forStreamWriter(streamWriter);
+ if (depth.isPresent()) {
+ return DepthAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth.get());
+ } else {
+ return RestconfDelegatingNormalizedNodeWriter.forStreamWriter(streamWriter);
+ }
}
private JsonWriter createJsonWriter(final OutputStream entityStream, boolean prettyPrint) {
*/
package org.opendaylight.controller.sal.rest.impl;
+import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.opendaylight.controller.sal.rest.api.Draft02;
+import org.opendaylight.controller.sal.rest.api.RestconfNormalizedNodeWriter;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
- writeNormalizedNode(xmlWriter,schemaPath,pathContext,data);
+ writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, t.getWriterParameters().getDepth());
+
}
- private void writeNormalizedNode(XMLStreamWriter xmlWriter, SchemaPath schemaPath,InstanceIdentifierContext<?> pathContext, NormalizedNode<?, ?> data) throws IOException {
- final NormalizedNodeWriter nnWriter;
+ private void writeNormalizedNode(XMLStreamWriter xmlWriter, SchemaPath schemaPath, InstanceIdentifierContext<?>
+ pathContext, NormalizedNode<?, ?> data, Optional<Integer> depth) throws IOException {
+ final RestconfNormalizedNodeWriter nnWriter;
final SchemaContext schemaCtx = pathContext.getSchemaContext();
if (SchemaPath.ROOT.equals(schemaPath)) {
- nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath);
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath, depth);
writeElements(xmlWriter, nnWriter, (ContainerNode) data);
} else if (pathContext.getSchemaNode() instanceof RpcDefinition) {
- nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, ((RpcDefinition) pathContext.getSchemaNode()).getOutput().getPath());
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx,
+ ((RpcDefinition) pathContext.getSchemaNode()).getOutput().getPath(), depth);
writeElements(xmlWriter, nnWriter, (ContainerNode) data);
} else {
- nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath.getParent());
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath.getParent(), depth);
if (data instanceof MapEntryNode) {
// Restconf allows returning one list item. We need to wrap it
// in map node in order to serialize it properly
nnWriter.flush();
}
- private NormalizedNodeWriter createNormalizedNodeWriter(XMLStreamWriter xmlWriter,
- SchemaContext schemaContext, SchemaPath schemaPath) {
+ private RestconfNormalizedNodeWriter createNormalizedNodeWriter(XMLStreamWriter xmlWriter,
+ SchemaContext schemaContext, SchemaPath schemaPath, Optional<Integer> depth) {
NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
- return NormalizedNodeWriter.forStreamWriter(xmlStreamWriter);
+ if (depth.isPresent()) {
+ return DepthAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth.get());
+ } else {
+ return RestconfDelegatingNormalizedNodeWriter.forStreamWriter(xmlStreamWriter);
+ }
}
- private void writeElements(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data)
+ private void writeElements(final XMLStreamWriter xmlWriter, final RestconfNormalizedNodeWriter nnWriter,
+ final ContainerNode data)
throws IOException {
try {
final QName name = data.getNodeType();
--- /dev/null
+/*
+ * Copyright (c) 2015 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.controller.sal.rest.impl;
+
+import java.io.IOException;
+import org.opendaylight.controller.sal.rest.api.RestconfNormalizedNodeWriter;
+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;
+
+/**
+ * This class just delegates all of the functionality to Yangtools normalized node writer
+ */
+public class RestconfDelegatingNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
+ private NormalizedNodeWriter delegNNWriter;
+
+ private RestconfDelegatingNormalizedNodeWriter(NormalizedNodeStreamWriter streamWriter, final boolean
+ orderKeyLeaves) {
+ this.delegNNWriter = NormalizedNodeWriter.forStreamWriter(streamWriter, orderKeyLeaves);
+ }
+
+ public static RestconfDelegatingNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+ return forStreamWriter(writer, true);
+ }
+
+ public static RestconfDelegatingNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, final boolean
+ orderKeyLeaves) {
+ return new RestconfDelegatingNormalizedNodeWriter(writer, orderKeyLeaves);
+ }
+
+ public RestconfDelegatingNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ delegNNWriter.write(node);
+ return this;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ delegNNWriter.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegNNWriter.close();
+ }
+}
+/*
+ * Copyright (c) 2015 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.controller.sal.restconf.impl;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
this.context = context;
this.data = data;
// default writer parameters
- this.writerParameters = new WriterParameters(false, Integer.MAX_VALUE);
+ this.writerParameters = new WriterParameters.WriterParametersBuilder().build();
}
public InstanceIdentifierContext<? extends SchemaNode> getInstanceIdentifierContext() {
-/**
+/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
}
}
- public static WriterParameters parseKnownWriterParameters(final UriInfo info) {
- boolean prettyPrint;
- int depth;
+ public static WriterParameters parseWriterParameters(final UriInfo info) {
+ WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder();
String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
- if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
- depth = Integer.MAX_VALUE;
- } else {
+ if (!Strings.isNullOrEmpty(param) && !"unbounded".equals(param)) {
try {
- depth = Integer.valueOf(param);
+ final int depth = Integer.valueOf(param);
if (depth < 1) {
throw new RestconfDocumentedException(new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
"Invalid depth parameter: " + depth, null,
"The depth parameter must be an integer > 1 or \"unbounded\""));
}
+ wpBuilder.setDepth(depth);
} catch (final NumberFormatException e) {
throw new RestconfDocumentedException(new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
"Invalid depth parameter: " + e.getMessage(), null,
}
}
param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
- prettyPrint = "true".equals(param);
- return new WriterParameters(prettyPrint, depth);
+ wpBuilder.setPrettyPrint("true".equals(param));
+ return wpBuilder.build();
}
}
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode,
null, schemaContext), moduleContainerBuilder.build(),
- QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ QueryParametersParser.parseWriterParameters(uriInfo));
}
/**
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode,
mountPoint, controllerContext.getGlobalSchema()), moduleContainerBuilder.build(),
- QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ QueryParametersParser.parseWriterParameters(uriInfo));
}
@Override
Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, moduleSchemaNode, mountPoint,
- schemaContext), moduleMap, QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ schemaContext), moduleMap, QueryParametersParser.parseWriterParameters(uriInfo));
}
@Override
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, streamsContainerSchemaNode, null,
- schemaContext), streamsContainerBuilder.build(), QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ schemaContext), streamsContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo));
}
@Override
return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
resultNodeSchema, mountPoint, schemaContext), resultData,
- QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ QueryParametersParser.parseWriterParameters(uriInfo));
}
private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
}
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, resultNodeSchema, mountPoint,
- schemaContext), resultData, QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ schemaContext), resultData, QueryParametersParser.parseWriterParameters(uriInfo));
}
private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
LOG.debug(errMsg + identifier);
throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
}
- return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo));
}
@Override
LOG.debug(errMsg + identifier);
throw new RestconfDocumentedException(errMsg , ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
}
- return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseKnownWriterParameters(uriInfo));
+ return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo));
}
@Override
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Optional;
+
public class WriterParameters {
- private final int depth;
+ private final Optional<Integer> depth;
private final boolean prettyPrint;
- public WriterParameters(final boolean prettyPrint, final int depth) {
- this.prettyPrint = prettyPrint;
- this.depth = depth;
+ private WriterParameters(final WriterParametersBuilder builder) {
+ this.prettyPrint = builder.prettyPrint;
+ this.depth = builder.depth;
}
- public int getDepth() {
+ public Optional<Integer> getDepth() {
return depth;
}
public boolean isPrettyPrint() {
return prettyPrint;
}
+
+ public static class WriterParametersBuilder {
+ private Optional<Integer> depth = Optional.absent();
+ private boolean prettyPrint;
+
+ public WriterParametersBuilder() {
+ }
+
+ public Optional<Integer> getDepth() {
+ return depth;
+ }
+
+ public WriterParametersBuilder setDepth(final int depth) {
+ this.depth = Optional.of(depth);
+ return this;
+ }
+
+ public boolean isPrettyPrint() {
+ return prettyPrint;
+ }
+
+ public WriterParametersBuilder setPrettyPrint(final boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ return this;
+ }
+
+ public WriterParameters build() {
+ return new WriterParameters(this);
+ }
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.BeforeClass;
+import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
+import org.opendaylight.controller.sal.rest.impl.XmlNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.controller.sal.restconf.impl.QueryParametersParser;
+import org.opendaylight.controller.sal.restconf.impl.WriterParameters;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class CutDataToCorrectDepthTest extends JerseyTest {
+
+ private static NormalizedNode<?, ?> depth1Cont;
+ private static NormalizedNode<?, ?> depth2Cont1;
+ private NormalizedNode<?, ?> globalPayload;
+ private static SchemaContext schemaContextModules;
+
+ @Path("/")
+ public class RestImpl {
+
+ @GET
+ @Path("/config/{identifier:.+}")
+ @Produces({ "application/json", "application/xml" })
+ public NormalizedNodeContext getData(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo uriInfo) {
+
+ final InstanceIdentifierContext iiWithData = ControllerContext.getInstance().toInstanceIdentifier(
+ identifier);
+
+ NormalizedNode<?, ?> data = null;
+ if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
+ data = depth2Cont1;
+ } else if (identifier.equals("nested-module:depth1-cont")) {
+ data = depth1Cont;
+ }
+
+ final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
+ return new NormalizedNodeContext(iiWithData, data, writerParameters);
+ }
+
+ @GET
+ @Path("/operational/{identifier:.+}")
+ @Produces({ "application/json", "application/xml" })
+ public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo uriInfo) {
+ return getData(identifier, uriInfo);
+ }
+
+ @PUT
+ @Path("/config/{identifier:.+}")
+ @Consumes({ "application/json", "application/xml" })
+ public void normalizedData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload) throws InterruptedException {
+ System.out.println(payload);
+ System.out.println(payload.getInstanceIdentifierContext().getInstanceIdentifier());
+ System.out.println(payload.getData());
+ globalPayload = payload.getData();
+ }
+
+ @PUT
+ @Path("/operational/{identifier:.+}")
+ @Consumes({ "application/json", "application/xml" })
+ public void normalizedDataOperational(@Encoded @PathParam("identifier") String identifier,
+ NormalizedNodeContext payload) throws InterruptedException {
+ normalizedData(identifier, payload);
+ }
+ }
+
+ @BeforeClass
+ public static void initialize() throws FileNotFoundException {
+ schemaContextModules = TestUtils.loadSchemaContext("/modules");
+ Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
+ assertNotNull(module);
+
+ UnkeyedListNode listAsUnkeyedList = unkeyedList(
+ "depth2-cont1",
+ unkeyedEntry("depth2-cont1",
+ container("depth3-cont1",
+ container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
+ leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
+
+ MapNode listAsMap = mapNode(
+ "depth2-list2",
+ mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+ leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
+
+ depth1Cont = container(
+ "depth1-cont",
+ listAsUnkeyedList,
+ listAsMap,
+ leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+ container(
+ "depth2-cont2",
+ container("depth3-cont2",
+ container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
+ leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
+ leaf("depth2-leaf1", "depth2-leaf1-value"));
+
+ depth2Cont1 = listAsUnkeyedList;
+ }
+
+ // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
+ // properly in readers
+ //@Test
+ public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
+ getDataWithUriDepthParameter("application/json");
+ getDataWithUriDepthParameter("application/xml");
+ }
+
+ public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
+ ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
+ Response response;
+
+ // Test config with depth 1
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
+ .get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth1());
+
+ // Test config with depth 2
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
+ .get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth2());
+
+ // Test config with depth 3
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
+ .get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth3());
+
+ // Test config with depth 4
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
+ .get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth4());
+
+ // Test config with depth 5
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
+ .get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth5());
+
+ // Test config with depth unbounded
+
+ response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
+ .request(mediaType).get();
+ txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+ verifyResponse(nodeDataDepth5());
+ }
+
+ private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
+ String responseStr = response.readEntity(String.class);
+ System.out.println(responseStr);
+ target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
+ }
+
+ private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
+ assertNotNull(globalPayload);
+ assertEquals(globalPayload, nodeData);
+ globalPayload = null;
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig();
+ resourceConfig = resourceConfig.registerInstances(new RestImpl());
+ resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
+ JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
+ RestconfDocumentedExceptionMapper.class);
+ return resourceConfig;
+ }
+
+ private static LeafNode<?> leaf(final String localName, final Object value) {
+ return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
+ }
+
+ private static ContainerNode container(final String localName, final DataContainerChild<?, ?>... children) {
+ DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders.containerBuilder();
+ for (DataContainerChild<?, ?> child : children) {
+ containerBuilder.withChild(child);
+ }
+ containerBuilder.withNodeIdentifier(toIdentifier(localName));
+ return containerBuilder.build();
+ }
+
+ private static UnkeyedListNode unkeyedList(
+ final String localName,
+ final UnkeyedListEntryNode... entryNodes) {
+ CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
+ final NodeIdentifier identifier = toIdentifier(localName);
+ builder.withNodeIdentifier(identifier);
+ for (UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
+ builder.withChild(unkeyedListEntryNode);
+ }
+ return builder.build();
+ }
+
+ private static UnkeyedListEntryNode unkeyedEntry(final String localName,
+ final DataContainerChild<?, ?>... children) {
+ DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = Builders.unkeyedListEntryBuilder();
+ builder.withNodeIdentifier(toIdentifier(localName));
+ for (DataContainerChild<?, ?> child : children) {
+ builder.withChild(child);
+ }
+ return builder.build();
+ }
+
+ private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) {
+ CollectionNodeBuilder<MapEntryNode, MapNode> builder = Builders.mapBuilder();
+ builder.withNodeIdentifier(toIdentifier(localName));
+ for (MapEntryNode mapEntryNode : entryNodes) {
+ builder.withChild(mapEntryNode);
+ }
+ return builder.build();
+ }
+
+ private static MapEntryNode mapEntryNode(final String localName, final int keysNumber,
+ final DataContainerChild<?, ?>... children) {
+ DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders.mapEntryBuilder();
+ Map<QName, Object> keys = new HashMap<>();
+ for (int i = 0; i < keysNumber; i++) {
+ keys.put(children[i].getNodeType(), children[i].getValue());
+ }
+ builder.withNodeIdentifier(toIdentifier(localName, keys));
+
+ for (DataContainerChild<?, ?> child : children) {
+ builder.withChild(child);
+ }
+ return builder.build();
+ }
+
+ private static LeafSetNode<?> leafList(final String localName, final String... children) {
+ ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
+ builder.withNodeIdentifier(toIdentifier(localName));
+ for (String child : children) {
+ builder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(toIdentifier(localName, child))
+ .withValue(child).build());
+ }
+ return builder.build();
+ }
+
+ private static NodeIdentifier toIdentifier(String localName) {
+ return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-3", localName));
+ }
+
+ private static NodeIdentifierWithPredicates toIdentifier(String localName, Map<QName, Object> keys) {
+ return new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-3", localName),
+ keys);
+ }
+
+ private static NodeWithValue toIdentifier(final String localName, final Object value) {
+ return new NodeWithValue(QName.create("urn:nested:module", "2014-06-3", localName), value);
+ }
+
+
+
+ private UnkeyedListEntryNode nodeDataDepth3Operational() {
+ return unkeyedEntry("depth2-cont1",
+ container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
+ leaf("depth3-leaf1", "depth3-leaf1-value"));
+ }
+
+ private ContainerNode nodeDataDepth5() {
+ return container(
+ "depth1-cont",
+ unkeyedList(
+ "depth2-cont1",
+ unkeyedEntry("depth2-cont1",
+ container("depth3-cont1",
+ container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
+ leaf("depth4-leaf1", "depth4-leaf1-value")),
+ leaf("depth3-leaf1", "depth3-leaf1-value"))),
+ mapNode("depth2-list2",
+ mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+ leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+ leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+ container(
+ "depth2-cont2",
+ container("depth3-cont2",
+ container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
+ leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
+ leaf("depth2-leaf1", "depth2-leaf1-value"));
+ }
+
+ private ContainerNode nodeDataDepth4() {
+ return container(
+ "depth1-cont",
+ unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
+ mapNode("depth2-list2",
+ mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+ leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+ leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+ container(
+ "depth2-cont2",
+ container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
+ leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
+ }
+
+ private ContainerNode nodeDataDepth3() {
+ return container(
+ "depth1-cont",
+ unkeyedList("depth2-cont1",
+ unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
+ mapNode("depth2-list2",
+ mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+ leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+ leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+ container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
+ leaf("depth2-leaf1", "depth2-leaf1-value"));
+ }
+
+ private ContainerNode nodeDataDepth2() {
+ return container(
+ "depth1-cont",
+ unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
+ mapNode("depth2-list2",
+ mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+ leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
+// leafList("depth2-lfLst1"),
+ leaf("depth2-leaf1", "depth2-leaf1-value"));
+ }
+
+ private ContainerNode nodeDataDepth1() {
+ return container("depth1-cont");
+ }
+}
revision "2014-06-3";
container depth1-cont {
- container depth2-cont1 {
+ list depth2-cont1 {
container depth3-cont1 {
container depth4-cont1 {
leaf depth5-leaf1 {
type string;
}
}
-
+
leaf depth4-leaf1 {
type string;
}
}
-
+
leaf depth3-leaf1 {
type string;
}
}
-
+
+ /* list depth2-list2 was added to test keyed list */
+ list depth2-list2 {
+ key "depth3-lf1-key depth3-lf2-key";
+ leaf depth3-lf1-key {
+ type string;
+ }
+ leaf depth3-lf2-key {
+ type string;
+ }
+ leaf depth3-lf3 {
+ type string;
+ }
+ }
+
+ leaf-list depth2-lfLst1 {
+ type string;
+ }
+
container depth2-cont2 {
container depth3-cont2 {
container depth4-cont2 {
type string;
}
}
-
+
leaf depth4-leaf2 {
type string;
}
}
-
+
leaf depth3-leaf2 {
type string;
}
}
-
+
leaf depth2-leaf1 {
type string;
}
}
-}
\ No newline at end of file
+}