Rename ChoiceCaseNode to CaseSchemaNode
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / SchemaOrderedNormalizedNodeWriter.java
1 /*
2  * Copyright (c) 2016 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 package org.opendaylight.yangtools.yang.data.impl.schema;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ArrayListMultimap;
12 import java.io.IOException;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.Objects;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
22 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
28 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, this iterates over elements
35  * in the order as they are defined in YANG file.
36  */
37 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
38     private static final Logger LOG = LoggerFactory.getLogger(SchemaOrderedNormalizedNodeWriter.class);
39     private final SchemaContext schemaContext;
40     private final SchemaNode root;
41     private final NormalizedNodeStreamWriter writer;
42
43     private SchemaNode currentSchemaNode;
44
45     /**
46      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
47      *
48      * @param writer
49      *            Back-end writer
50      * @param schemaContext
51      *            Schema context
52      * @param path
53      *            path
54      */
55     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
56             final SchemaPath path) {
57         super(writer);
58         this.writer = writer;
59         this.schemaContext = schemaContext;
60         final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(schemaContext, path);
61         Preconditions.checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s",
62                 path);
63         if (schemaNodes.size() > 1) {
64             LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
65         }
66         this.root = schemaNodes.iterator().next();
67     }
68
69     @Override
70     public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
71         if (Objects.equals(root, schemaContext)) {
72             currentSchemaNode = schemaContext.getDataChildByName(node.getNodeType());
73         } else {
74             currentSchemaNode = root;
75         }
76         return write(node, currentSchemaNode);
77     }
78
79     /**
80      * Iterate over the provided collection and emit write
81      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
82      *
83      * @param nodes nodes
84      * @return NormalizedNodeWriter this
85      * @throws IOException when thrown from the backing writer.
86      */
87     public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
88         currentSchemaNode = root;
89         if (writeChildren(nodes, currentSchemaNode, false)) {
90             return this;
91         }
92
93         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
94     }
95
96     private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode)
97             throws IOException {
98
99         //Set current schemaNode
100         try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
101             if (node == null) {
102                 return this;
103             }
104
105             if (wasProcessedAsCompositeNode(node)) {
106                 return this;
107             }
108
109             if (wasProcessAsSimpleNode(node)) {
110                 return this;
111             }
112         }
113
114         throw new IllegalStateException("It wasn't possible to serialize node " + node);
115     }
116
117     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
118         for (final NormalizedNode<?, ?> node : nodes) {
119             write(node, dataSchemaNode);
120         }
121     }
122
123     @Override
124     protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
125         return writeChildren(children, currentSchemaNode, true);
126     }
127
128     private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
129             final SchemaNode parentSchemaNode, final boolean endParent) throws IOException {
130         //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
131         final ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
132         for (final NormalizedNode<?, ?> child : children) {
133             if (child instanceof AugmentationNode) {
134                 qNameToNodes.putAll(resolveAugmentations(child));
135             } else {
136                 qNameToNodes.put(child.getNodeType(), child);
137             }
138         }
139
140         if (parentSchemaNode instanceof DataNodeContainer) {
141             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
142                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
143             } else {
144                 for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
145                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
146                 }
147             }
148         } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
149             for (final CaseSchemaNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases().values()) {
150                 for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
151                     if (qNameToNodes.containsKey(dsn.getQName())) {
152                         write(qNameToNodes.get(dsn.getQName()), dsn);
153                     }
154                 }
155             }
156         } else {
157             for (final NormalizedNode<?, ?> child : children) {
158                 writeLeaf(child);
159             }
160         }
161         if (endParent) {
162             writer.endNode();
163         }
164         return true;
165     }
166
167     private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
168         if (wasProcessAsSimpleNode(node)) {
169             return this;
170         }
171
172         throw new IllegalStateException("It wasn't possible to serialize node " + node);
173     }
174
175     private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
176         final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
177         for (final NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
178             if (node instanceof AugmentationNode) {
179                 resolvedAugs.putAll(resolveAugmentations(node));
180             } else {
181                 resolvedAugs.put(node.getNodeType(), node);
182             }
183         }
184         return resolvedAugs;
185     }
186
187     private final class SchemaNodeSetter implements AutoCloseable {
188
189         private final SchemaNode previousSchemaNode;
190
191         /**
192          * Sets current schema node new value and store old value for later restore.
193          */
194         SchemaNodeSetter(final SchemaNode schemaNode) {
195             previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode;
196             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode;
197         }
198
199         /**
200          * Restore previous schema node.
201          */
202         @Override
203         public void close() {
204             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode;
205         }
206     }
207 }