Mark deprecated methods for removal
[yangtools.git] / data / 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 static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.ArrayListMultimap;
14 import com.google.common.collect.Multimap;
15 import java.io.IOException;
16 import java.util.Collection;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
23 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
31 import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
32 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
34 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
35
36 /**
37  * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, this iterates over elements
38  * in the order as they are defined in YANG file.
39  */
40 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
41     private final EffectiveModelContext schemaContext;
42     private final SchemaNode root;
43
44     private SchemaNode currentSchemaNode;
45
46     /**
47      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
48      *
49      * @param writer Back-end writer
50      * @param schemaContext Associated {@link EffectiveModelContext}
51      */
52     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
53             final EffectiveModelContext schemaContext) {
54         super(writer);
55         root = this.schemaContext = requireNonNull(schemaContext);
56     }
57
58     private SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
59             final SchemaInferenceStack stack) {
60         super(writer);
61         schemaContext = stack.getEffectiveModelContext();
62
63         if (!stack.isEmpty()) {
64             final EffectiveStatement<?, ?> current = stack.currentStatement();
65             // FIXME: this should be one of NormalizedNodeContainer/NotificationDefinition/OperationDefinition
66             checkArgument(current instanceof SchemaNode, "Instantiating at %s is not supported", current);
67             root = (SchemaNode) current;
68         } else {
69             root = schemaContext;
70         }
71     }
72
73     /**
74      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
75      *
76      * @param writer Back-end writer
77      * @param schemaContext Associated {@link EffectiveModelContext}
78      * @param path root path
79      */
80     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
81             final EffectiveModelContext schemaContext, final Absolute path) {
82         this(writer, SchemaInferenceStack.of(schemaContext, path));
83     }
84
85     /**
86      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
87      *
88      * @param writer Back-end writer
89      * @param rootInference A SchemaTreeInference pointing to the root element
90      */
91     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
92             final SchemaTreeInference rootInference) {
93         this(writer, SchemaInferenceStack.ofInference(rootInference));
94     }
95
96     /**
97      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
98      *
99      * @param writer Back-end writer
100      * @param schemaContext Associated {@link EffectiveModelContext}
101      * @param path root path
102      * @deprecated Use either one of the alternative constructors instead.
103      */
104     @Deprecated(since = "7.0.11", forRemoval = true)
105     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
106             final EffectiveModelContext schemaContext, final SchemaPath path) {
107         this(writer, SchemaInferenceStack.ofSchemaPath(schemaContext, path));
108     }
109
110     @Override
111     public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node) throws IOException {
112         if (schemaContext.equals(root)) {
113             currentSchemaNode = schemaContext.dataChildByName(node.getIdentifier().getNodeType());
114         } else {
115             currentSchemaNode = root;
116         }
117         return write(node, currentSchemaNode);
118     }
119
120     /**
121      * Iterate over the provided collection and emit write
122      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
123      *
124      * @param nodes nodes
125      * @return NormalizedNodeWriter this
126      * @throws IOException when thrown from the backing writer.
127      */
128     public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild> nodes) throws IOException {
129         currentSchemaNode = root;
130         if (writeChildren(nodes, currentSchemaNode, false)) {
131             return this;
132         }
133
134         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
135     }
136
137     private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node, final SchemaNode dataSchemaNode)
138             throws IOException {
139
140         //Set current schemaNode
141         try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
142             if (node == null) {
143                 return this;
144             }
145
146             if (wasProcessedAsCompositeNode(node)) {
147                 return this;
148             }
149
150             if (wasProcessAsSimpleNode(node)) {
151                 return this;
152             }
153         }
154
155         throw new IllegalStateException("It wasn't possible to serialize node " + node);
156     }
157
158     private void write(final Collection<NormalizedNode> nodes, final SchemaNode dataSchemaNode) throws IOException {
159         for (final NormalizedNode node : nodes) {
160             write(node, dataSchemaNode);
161         }
162     }
163
164     @Override
165     protected boolean writeChildren(final Iterable<? extends NormalizedNode> children) throws IOException {
166         return writeChildren(children, currentSchemaNode, true);
167     }
168
169     private boolean writeChildren(final Iterable<? extends NormalizedNode> children, final SchemaNode parentSchemaNode,
170             final boolean endParent) throws IOException {
171         // Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
172         final Multimap<QName, NormalizedNode> qnameToNodes = ArrayListMultimap.create();
173         for (final NormalizedNode child : children) {
174             putChild(qnameToNodes, child);
175         }
176
177         if (parentSchemaNode instanceof DataNodeContainer) {
178             if (parentSchemaNode instanceof ListSchemaNode && qnameToNodes.containsKey(parentSchemaNode.getQName())) {
179                 write(qnameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
180             } else {
181                 for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
182                     write(qnameToNodes.get(schemaNode.getQName()), schemaNode);
183                 }
184             }
185         } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
186             for (final CaseSchemaNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
187                 for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
188                     if (qnameToNodes.containsKey(dsn.getQName())) {
189                         write(qnameToNodes.get(dsn.getQName()), dsn);
190                     }
191                 }
192             }
193         } else {
194             for (final NormalizedNode child : children) {
195                 writeLeaf(child);
196             }
197         }
198         if (endParent) {
199             getWriter().endNode();
200         }
201         return true;
202     }
203
204     private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode node) throws IOException {
205         if (wasProcessAsSimpleNode(node)) {
206             return this;
207         }
208
209         throw new IllegalStateException("It wasn't possible to serialize node " + node);
210     }
211
212     private static void putChild(final Multimap<QName, NormalizedNode> qnameToNodes, final NormalizedNode child) {
213         if (child instanceof AugmentationNode) {
214             for (DataContainerChild grandChild : ((AugmentationNode) child).body()) {
215                 putChild(qnameToNodes, grandChild);
216             }
217         } else {
218             qnameToNodes.put(child.getIdentifier().getNodeType(), child);
219         }
220     }
221
222     private final class SchemaNodeSetter implements AutoCloseable {
223
224         private final SchemaNode previousSchemaNode;
225
226         /**
227          * Sets current schema node new value and store old value for later restore.
228          */
229         SchemaNodeSetter(final SchemaNode schemaNode) {
230             previousSchemaNode = currentSchemaNode;
231             currentSchemaNode = schemaNode;
232         }
233
234         /**
235          * Restore previous schema node.
236          */
237         @Override
238         public void close() {
239             currentSchemaNode = previousSchemaNode;
240         }
241     }
242 }