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