Fix netconf-connector-config groupId
[netconf.git] / opendaylight / netconf / netconf-util / src / main / java / org / opendaylight / netconf / util / OrderedNormalizedNodeWriter.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netconf.util;
10
11 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
12
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;
18
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;
25
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;
53
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{
57
58     private final SchemaContext schemaContext;
59     private final SchemaNode root;
60     private final NormalizedNodeStreamWriter writer;
61
62     public OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,final SchemaContext schemaContext,final SchemaPath path) {
63         this.writer = writer;
64         this.schemaContext = schemaContext;
65         this.root = findParentSchemaOnPath(schemaContext, path);
66     }
67
68     public OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
69         if (Objects.equals(root, schemaContext)) {
70             return write(node, schemaContext.getDataChildByName(node.getNodeType()));
71         }
72
73         return write(node, root);
74     }
75
76     public OrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
77         if (writeChildren(nodes, root, false)) {
78             return this;
79         }
80
81         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
82
83     }
84
85     private OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
86         if (node == null) {
87             return this;
88         }
89
90         if (wasProcessedAsCompositeNode(node, dataSchemaNode)) {
91             return this;
92         }
93
94         if (wasProcessAsSimpleNode(node)) {
95             return this;
96         }
97
98         throw new IllegalStateException("It wasn't possible to serialize node " + node);
99     }
100
101     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
102         for (NormalizedNode<?, ?> node : nodes) {
103             write(node, dataSchemaNode);
104         }
105     }
106
107     private OrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
108         if (wasProcessAsSimpleNode(node)) {
109             return this;
110         }
111
112         throw new IllegalStateException("It wasn't possible to serialize node " + node);
113     }
114
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));
121             } else {
122                 qNameToNodes.put(child.getNodeType(), child);
123             }
124         }
125
126         if (parentSchemaNode instanceof DataNodeContainer) {
127             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
128                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
129             } else {
130                 for (DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
131                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
132                 }
133             }
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);
139                     }
140                 }
141             }
142         } else {
143             for (NormalizedNode<?, ?> child : children) {
144                 writeLeaf(child);
145             }
146         }
147         if (endParent) {
148             writer.endNode();
149         }
150         return true;
151     }
152
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));
158             } else {
159                 resolvedAugs.put(node.getNodeType(), node);
160             }
161         }
162         return resolvedAugs;
163     }
164
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());
169         } else {
170             writer.startMapEntryNode(node.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(node.getValue()));
171         }
172         return writeChildren(node.getValue(), dataSchemaNode, true);
173     }
174
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());
181             } else {
182                 writer.leafSetEntryNode(nodeAsLeafList.getNodeType(), nodeAsLeafList.getValue());
183             }
184             return true;
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());
189             } else {
190                 writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
191             }
192             return true;
193         } else if (node instanceof AnyXmlNode) {
194             final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
195             writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
196             return true;
197         }
198
199         return false;
200     }
201
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());
207             } else {
208                 writer.startContainerNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
209             }
210             return writeChildren(n.getValue(), dataSchemaNode, true);
211         }
212         if (node instanceof MapEntryNode) {
213             return writeMapEntryNode((MapEntryNode) node, dataSchemaNode);
214         }
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);
219         }
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);
224         }
225         if (node instanceof AugmentationNode) {
226             final AugmentationNode n = (AugmentationNode) node;
227             writer.startAugmentationNode(n.getIdentifier());
228             return writeChildren(n.getValue(), dataSchemaNode, true);
229         }
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);
234         }
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);
239         }
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);
244         }
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()));
249             } else {
250                 writer.startLeafSet(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
251             }
252             return writeChildren(n.getValue(), dataSchemaNode, true);
253         }
254
255         return false;
256     }
257
258     private static final int childSizeHint(final Iterable<?> children) {
259         return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
260     }
261
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()) {
266             SchemaNode child;
267             if(current instanceof DataNodeContainer) {
268                 child = ((DataNodeContainer) current).getDataChildByName(qname);
269
270                 if (child == null && current instanceof SchemaContext) {
271                     child = tryFindGroupings((SchemaContext) current, qname).orNull();
272                 }
273
274                 if(child == null && current instanceof SchemaContext) {
275                     child = tryFindNotification((SchemaContext) current, qname)
276                             .or(tryFindRpc(((SchemaContext) current), qname)).orNull();
277                 }
278             } else if (current instanceof ChoiceSchemaNode) {
279                 child = ((ChoiceSchemaNode) current).getCaseNodeByName(qname);
280             } else if (current instanceof RpcDefinition) {
281                 switch (qname.getLocalName()) {
282                 case "input":
283                     child = ((RpcDefinition) current).getInput();
284                     break;
285                 case "output":
286                     child = ((RpcDefinition) current).getOutput();
287                     break;
288                 default:
289                     child = null;
290                     break;
291                 }
292             } else {
293                 throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", current));
294             }
295             current = child;
296         }
297         return current;
298     }
299
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));
303     }
304
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));
308     }
309
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));
313     }
314
315     @Override
316     public void flush() throws IOException {
317         writer.flush();
318     }
319
320     @Override
321     public void close() throws IOException {
322         writer.flush();
323         writer.close();
324     }
325
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;
329
330         public SchemaNodePredicate(final QName qname) {
331             this.qname = qname;
332         }
333
334         @Override
335         public boolean apply(final SchemaNode input) {
336             return input.getQName().equals(qname);
337         }
338     }
339 }