Bug 3896 - direct usage of == instead of .equals
[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.OrderedMapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
42 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
43 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
44 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
46 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
49 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
50 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
52
53 //TODO this does not extend NormalizedNodeWriter from yangtools due to api freeze, make this inherit common methods to avoid code duplication
54 //TODO move this to yangtools, since this is in netconf-util due to api freeze in lithium
55 public class OrderedNormalizedNodeWriter implements Closeable, Flushable{
56
57     private final SchemaContext schemaContext;
58     private final SchemaNode root;
59     private final NormalizedNodeStreamWriter writer;
60
61     public OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,final SchemaContext schemaContext,final SchemaPath path) {
62         this.writer = writer;
63         this.schemaContext = schemaContext;
64         this.root = findParentSchemaOnPath(schemaContext, path);
65     }
66
67     public OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
68         if (Objects.equals(root, schemaContext)) {
69             return write(node, schemaContext.getDataChildByName(node.getNodeType()));
70         }
71
72         return write(node, root);
73     }
74
75     public OrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
76         if (writeChildren(nodes, root, false)) {
77             return this;
78         }
79
80         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
81
82     }
83
84     private OrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
85         if (node == null) {
86             return this;
87         }
88
89         if (wasProcessedAsCompositeNode(node, dataSchemaNode)) {
90             return this;
91         }
92
93         if (wasProcessAsSimpleNode(node)) {
94             return this;
95         }
96
97         throw new IllegalStateException("It wasn't possible to serialize node " + node);
98     }
99
100     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
101         for (NormalizedNode<?, ?> node : nodes) {
102             write(node, dataSchemaNode);
103         }
104     }
105
106     private OrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
107         if (wasProcessAsSimpleNode(node)) {
108             return this;
109         }
110
111         throw new IllegalStateException("It wasn't possible to serialize node " + node);
112     }
113
114     private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children, final SchemaNode parentSchemaNode, boolean endParent) throws IOException {
115         //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
116         ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
117         for (NormalizedNode<?, ?> child : children) {
118             if (child instanceof AugmentationNode) {
119                 qNameToNodes.putAll(resolveAugmentations(child));
120             } else {
121                 qNameToNodes.put(child.getNodeType(), child);
122             }
123         }
124
125         if (parentSchemaNode instanceof DataNodeContainer) {
126             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
127                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
128             } else {
129                 for (DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
130                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
131                 }
132             }
133         } else if(parentSchemaNode instanceof ChoiceSchemaNode) {
134             for (ChoiceCaseNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
135                 for (DataSchemaNode dsn : ccNode.getChildNodes()) {
136                     if (qNameToNodes.containsKey(dsn.getQName())) {
137                         write(qNameToNodes.get(dsn.getQName()), dsn);
138                     }
139                 }
140             }
141         } else {
142             for (NormalizedNode<?, ?> child : children) {
143                 writeLeaf(child);
144             }
145         }
146         if (endParent) {
147             writer.endNode();
148         }
149         return true;
150     }
151
152     private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
153         final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
154         for (NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
155             if (node instanceof AugmentationNode) {
156                 resolvedAugs.putAll(resolveAugmentations(node));
157             } else {
158                 resolvedAugs.put(node.getNodeType(), node);
159             }
160         }
161         return resolvedAugs;
162     }
163
164     private boolean writeMapEntryNode(final MapEntryNode node, final SchemaNode dataSchemaNode) throws IOException {
165         if(writer instanceof NormalizedNodeStreamAttributeWriter) {
166             ((NormalizedNodeStreamAttributeWriter) writer)
167                     .startMapEntryNode(node.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(node.getValue()), node.getAttributes());
168         } else {
169             writer.startMapEntryNode(node.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(node.getValue()));
170         }
171         return writeChildren(node.getValue(), dataSchemaNode, true);
172     }
173
174     private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
175         if (node instanceof LeafSetEntryNode) {
176             final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
177             if(writer instanceof NormalizedNodeStreamAttributeWriter) {
178                 ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
179             } else {
180                 writer.leafSetEntryNode(nodeAsLeafList.getValue());
181             }
182             return true;
183         } else if (node instanceof LeafNode) {
184             final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
185             if(writer instanceof NormalizedNodeStreamAttributeWriter) {
186                 ((NormalizedNodeStreamAttributeWriter) writer).leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
187             } else {
188                 writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
189             }
190             return true;
191         } else if (node instanceof AnyXmlNode) {
192             final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
193             writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
194             return true;
195         }
196
197         return false;
198     }
199
200     private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
201         if (node instanceof ContainerNode) {
202             final ContainerNode n = (ContainerNode) node;
203             if(writer instanceof NormalizedNodeStreamAttributeWriter) {
204                 ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()), n.getAttributes());
205             } else {
206                 writer.startContainerNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
207             }
208             return writeChildren(n.getValue(), dataSchemaNode, true);
209         }
210         if (node instanceof MapEntryNode) {
211             return writeMapEntryNode((MapEntryNode) node, dataSchemaNode);
212         }
213         if (node instanceof UnkeyedListEntryNode) {
214             final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
215             writer.startUnkeyedListItem(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
216             return writeChildren(n.getValue(), dataSchemaNode, true);
217         }
218         if (node instanceof ChoiceNode) {
219             final ChoiceNode n = (ChoiceNode) node;
220             writer.startChoiceNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
221             return writeChildren(n.getValue(), dataSchemaNode, true);
222         }
223         if (node instanceof AugmentationNode) {
224             final AugmentationNode n = (AugmentationNode) node;
225             writer.startAugmentationNode(n.getIdentifier());
226             return writeChildren(n.getValue(), dataSchemaNode, true);
227         }
228         if (node instanceof UnkeyedListNode) {
229             final UnkeyedListNode n = (UnkeyedListNode) node;
230             writer.startUnkeyedList(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
231             return writeChildren(n.getValue(), dataSchemaNode, true);
232         }
233         if (node instanceof OrderedMapNode) {
234             final OrderedMapNode n = (OrderedMapNode) node;
235             writer.startOrderedMapNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
236             return writeChildren(n.getValue(), dataSchemaNode, true);
237         }
238         if (node instanceof MapNode) {
239             final MapNode n = (MapNode) node;
240             writer.startMapNode(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
241             return writeChildren(n.getValue(), dataSchemaNode, true);
242         }
243         if (node instanceof LeafSetNode) {
244             //covers also OrderedLeafSetNode for which doesn't exist start* method
245             final LeafSetNode<?> n = (LeafSetNode<?>) node;
246             writer.startLeafSet(n.getIdentifier(), OrderedNormalizedNodeWriter.childSizeHint(n.getValue()));
247             return writeChildren(n.getValue(), dataSchemaNode, true);
248         }
249
250         return false;
251     }
252
253     private static final int childSizeHint(final Iterable<?> children) {
254         return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
255     }
256
257     //TODO similar code is already present in schemaTracker, unify this when this writer is moved back to yangtools
258     private SchemaNode findParentSchemaOnPath(final SchemaContext schemaContext, final SchemaPath path) {
259         SchemaNode current = Preconditions.checkNotNull(schemaContext);
260         for (final QName qname : path.getPathFromRoot()) {
261             SchemaNode child;
262             if(current instanceof DataNodeContainer) {
263                 child = ((DataNodeContainer) current).getDataChildByName(qname);
264
265                 if (child == null && current instanceof SchemaContext) {
266                     child = tryFindGroupings((SchemaContext) current, qname).orNull();
267                 }
268
269                 if(child == null && current instanceof SchemaContext) {
270                     child = tryFindNotification((SchemaContext) current, qname)
271                             .or(tryFindRpc(((SchemaContext) current), qname)).orNull();
272                 }
273             } else if (current instanceof ChoiceSchemaNode) {
274                 child = ((ChoiceSchemaNode) current).getCaseNodeByName(qname);
275             } else if (current instanceof RpcDefinition) {
276                 switch (qname.getLocalName()) {
277                 case "input":
278                     child = ((RpcDefinition) current).getInput();
279                     break;
280                 case "output":
281                     child = ((RpcDefinition) current).getOutput();
282                     break;
283                 default:
284                     child = null;
285                     break;
286                 }
287             } else {
288                 throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", current));
289             }
290             current = child;
291         }
292         return current;
293     }
294
295     //TODO this method is already present in schemaTracker, unify this when this writer is moved back to yangtools
296     private Optional<SchemaNode> tryFindGroupings(final SchemaContext ctx, final QName qname) {
297         return Optional.<SchemaNode> fromNullable(Iterables.find(ctx.getGroupings(), new SchemaNodePredicate(qname), null));
298     }
299
300     //TODO this method is already present in schemaTracker, unify this when this writer is moved back to yangtools
301     private Optional<SchemaNode> tryFindRpc(final SchemaContext ctx, final QName qname) {
302         return Optional.<SchemaNode>fromNullable(Iterables.find(ctx.getOperations(), 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 Optional<SchemaNode> tryFindNotification(final SchemaContext ctx, final QName qname) {
307         return Optional.<SchemaNode>fromNullable(Iterables.find(ctx.getNotifications(), new SchemaNodePredicate(qname), null));
308     }
309
310     @Override
311     public void flush() throws IOException {
312         writer.flush();
313     }
314
315     @Override
316     public void close() throws IOException {
317         writer.flush();
318         writer.close();
319     }
320
321     //TODO this class is already present in schemaTracker, unify this when this writer is moved back to yangtools
322     private static final class SchemaNodePredicate implements Predicate<SchemaNode> {
323         private final QName qname;
324
325         public SchemaNodePredicate(final QName qname) {
326             this.qname = qname;
327         }
328
329         @Override
330         public boolean apply(final SchemaNode input) {
331             return input.getQName().equals(qname);
332         }
333     }
334 }