2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netconf.util;
11 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.base.Predicate;
16 import com.google.common.collect.ArrayListMultimap;
17 import com.google.common.collect.Iterables;
19 import java.io.Closeable;
20 import java.io.Flushable;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Objects;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
43 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
44 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
45 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
47 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
54 //TODO this does not extend NormalizedNodeWriter from yangtools due to api freeze, make this inherit common methods to avoid code duplication
55 //TODO move this to yangtools, since this is in netconf-util due to api freeze in lithium
56 public class OrderedNormalizedNodeWriter implements Closeable, Flushable{
58 private final SchemaContext schemaContext;
59 private final SchemaNode root;
60 private final NormalizedNodeStreamWriter writer;
62 public OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,final SchemaContext schemaContext,final SchemaPath path) {
64 this.schemaContext = schemaContext;
65 this.root = findParentSchemaOnPath(schemaContext, path);
68 public OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
69 if (Objects.equals(root, schemaContext)) {
70 return write(node, schemaContext.getDataChildByName(node.getNodeType()));
73 return write(node, root);
76 public OrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
77 if (writeChildren(nodes, root, false)) {
81 throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
85 private OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
90 if (wasProcessedAsCompositeNode(node, dataSchemaNode)) {
94 if (wasProcessAsSimpleNode(node)) {
98 throw new IllegalStateException("It wasn't possible to serialize node " + node);
101 private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
102 for (NormalizedNode<?, ?> node : nodes) {
103 write(node, dataSchemaNode);
107 private OrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
108 if (wasProcessAsSimpleNode(node)) {
112 throw new IllegalStateException("It wasn't possible to serialize node " + node);
115 private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children, final SchemaNode parentSchemaNode, boolean endParent) throws IOException {
116 //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
117 ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
118 for (NormalizedNode<?, ?> child : children) {
119 if (child instanceof AugmentationNode) {
120 qNameToNodes.putAll(resolveAugmentations(child));
122 qNameToNodes.put(child.getNodeType(), child);
126 if (parentSchemaNode instanceof DataNodeContainer) {
127 if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
128 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
130 for (DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
131 write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
134 } else if(parentSchemaNode instanceof ChoiceSchemaNode) {
135 for (ChoiceCaseNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
136 for (DataSchemaNode dsn : ccNode.getChildNodes()) {
137 if (qNameToNodes.containsKey(dsn.getQName())) {
138 write(qNameToNodes.get(dsn.getQName()), dsn);
143 for (NormalizedNode<?, ?> child : children) {
153 private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
154 final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
155 for (NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
156 if (node instanceof AugmentationNode) {
157 resolvedAugs.putAll(resolveAugmentations(node));
159 resolvedAugs.put(node.getNodeType(), node);
165 private boolean writeMapEntryNode(final MapEntryNode node, final SchemaNode dataSchemaNode) throws IOException {
166 if(writer instanceof NormalizedNodeStreamAttributeWriter) {
167 ((NormalizedNodeStreamAttributeWriter) writer)
168 .startMapEntryNode(node.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(node.getValue()), node.getAttributes());
170 writer.startMapEntryNode(node.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(node.getValue()));
172 return writeChildren(node.getValue(), dataSchemaNode, true);
175 private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
176 if (node instanceof LeafSetEntryNode) {
177 final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
178 if(writer instanceof NormalizedNodeStreamAttributeWriter) {
179 ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getNodeType(),
180 nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
182 writer.leafSetEntryNode(nodeAsLeafList.getNodeType(), nodeAsLeafList.getValue());
185 } else if (node instanceof LeafNode) {
186 final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
187 if(writer instanceof NormalizedNodeStreamAttributeWriter) {
188 ((NormalizedNodeStreamAttributeWriter) writer).leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
190 writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
193 } else if (node instanceof AnyXmlNode) {
194 final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
195 writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
202 private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
203 if (node instanceof ContainerNode) {
204 final ContainerNode n = (ContainerNode) node;
205 if(writer instanceof NormalizedNodeStreamAttributeWriter) {
206 ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()), n.getAttributes());
208 writer.startContainerNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
210 return writeChildren(n.getValue(), dataSchemaNode, true);
212 if (node instanceof MapEntryNode) {
213 return writeMapEntryNode((MapEntryNode) node, dataSchemaNode);
215 if (node instanceof UnkeyedListEntryNode) {
216 final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
217 writer.startUnkeyedListItem(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
218 return writeChildren(n.getValue(), dataSchemaNode, true);
220 if (node instanceof ChoiceNode) {
221 final ChoiceNode n = (ChoiceNode) node;
222 writer.startChoiceNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
223 return writeChildren(n.getValue(), dataSchemaNode, true);
225 if (node instanceof AugmentationNode) {
226 final AugmentationNode n = (AugmentationNode) node;
227 writer.startAugmentationNode(n.getIdentifier());
228 return writeChildren(n.getValue(), dataSchemaNode, true);
230 if (node instanceof UnkeyedListNode) {
231 final UnkeyedListNode n = (UnkeyedListNode) node;
232 writer.startUnkeyedList(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
233 return writeChildren(n.getValue(), dataSchemaNode, true);
235 if (node instanceof OrderedMapNode) {
236 final OrderedMapNode n = (OrderedMapNode) node;
237 writer.startOrderedMapNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
238 return writeChildren(n.getValue(), dataSchemaNode, true);
240 if (node instanceof MapNode) {
241 final MapNode n = (MapNode) node;
242 writer.startMapNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
243 return writeChildren(n.getValue(), dataSchemaNode, true);
245 if (node instanceof LeafSetNode) {
246 final LeafSetNode<?> n = (LeafSetNode<?>) node;
247 if (node instanceof OrderedLeafSetNode) {
248 writer.startOrderedLeafSet(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
250 writer.startLeafSet(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
252 return writeChildren(n.getValue(), dataSchemaNode, true);
258 private static final int childSizeHint(final Iterable<?> children) {
259 return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
262 //TODO similar code is already present in schemaTracker, unify this when this writer is moved back to yangtools
263 private static SchemaNode findParentSchemaOnPath(final SchemaContext schemaContext, final SchemaPath path) {
264 SchemaNode current = Preconditions.checkNotNull(schemaContext);
265 for (final QName qname : path.getPathFromRoot()) {
267 if(current instanceof DataNodeContainer) {
268 child = ((DataNodeContainer) current).getDataChildByName(qname);
270 if (child == null && current instanceof SchemaContext) {
271 child = tryFindGroupings((SchemaContext) current, qname).orNull();
274 if(child == null && current instanceof SchemaContext) {
275 child = tryFindNotification((SchemaContext) current, qname)
276 .or(tryFindRpc(((SchemaContext) current), qname)).orNull();
278 } else if (current instanceof ChoiceSchemaNode) {
279 child = ((ChoiceSchemaNode) current).getCaseNodeByName(qname);
280 } else if (current instanceof RpcDefinition) {
281 switch (qname.getLocalName()) {
283 child = ((RpcDefinition) current).getInput();
286 child = ((RpcDefinition) current).getOutput();
293 throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", current));
300 //TODO this method is already present in schemaTracker, unify this when this writer is moved back to yangtools
301 private static Optional<SchemaNode> tryFindGroupings(final SchemaContext ctx, final QName qname) {
302 return Optional.<SchemaNode> fromNullable(Iterables.find(ctx.getGroupings(), new SchemaNodePredicate(qname), null));
305 //TODO this method is already present in schemaTracker, unify this when this writer is moved back to yangtools
306 private static Optional<SchemaNode> tryFindRpc(final SchemaContext ctx, final QName qname) {
307 return Optional.<SchemaNode>fromNullable(Iterables.find(ctx.getOperations(), new SchemaNodePredicate(qname), null));
310 //TODO this method is already present in schemaTracker, unify this when this writer is moved back to yangtools
311 private static Optional<SchemaNode> tryFindNotification(final SchemaContext ctx, final QName qname) {
312 return Optional.<SchemaNode>fromNullable(Iterables.find(ctx.getNotifications(), new SchemaNodePredicate(qname), null));
316 public void flush() throws IOException {
321 public void close() throws IOException {
326 //TODO this class is already present in schemaTracker, unify this when this writer is moved back to yangtools
327 private static final class SchemaNodePredicate implements Predicate<SchemaNode> {
328 private final QName qname;
330 public SchemaNodePredicate(final QName qname) {
335 public boolean apply(final SchemaNode input) {
336 return input.getQName().equals(qname);