43a51e19efb7aa23bab41e9a78ed069ad8c6acc2
[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 static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.ArrayListMultimap;
14 import java.io.IOException;
15 import java.util.Collection;
16 import java.util.List;
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.meta.EffectiveStatement;
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         this.root = this.schemaContext = requireNonNull(schemaContext);
54     }
55
56     /**
57      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
58      *
59      * @param writer Back-end writer
60      * @param schemaContext Associated {@link EffectiveModelContext}
61      * @param path root path
62      */
63     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
64             final EffectiveModelContext schemaContext, final SchemaPath path) {
65         super(writer);
66         this.schemaContext = requireNonNull(schemaContext);
67
68         final SchemaInferenceStack stack = SchemaInferenceStack.ofInstantiatedPath(schemaContext, path);
69         if (!stack.isEmpty()) {
70             final EffectiveStatement<?, ?> current = stack.currentStatement();
71             // FIXME: this should be one of NormalizedNodeContainer/NotificationDefinition/OperationDefinition
72             checkArgument(current instanceof SchemaNode, "Instantiating at %s is not supported", current);
73             root = (SchemaNode) current;
74         } else {
75             root = schemaContext;
76         }
77     }
78
79     @Override
80     public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node) throws IOException {
81         if (schemaContext.equals(root)) {
82             currentSchemaNode = schemaContext.dataChildByName(node.getNodeType());
83         } else {
84             currentSchemaNode = root;
85         }
86         return write(node, currentSchemaNode);
87     }
88
89     /**
90      * Iterate over the provided collection and emit write
91      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
92      *
93      * @param nodes nodes
94      * @return NormalizedNodeWriter this
95      * @throws IOException when thrown from the backing writer.
96      */
97     public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild> nodes) throws IOException {
98         currentSchemaNode = root;
99         if (writeChildren(nodes, currentSchemaNode, false)) {
100             return this;
101         }
102
103         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
104     }
105
106     private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node, final SchemaNode dataSchemaNode)
107             throws IOException {
108
109         //Set current schemaNode
110         try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
111             if (node == null) {
112                 return this;
113             }
114
115             if (wasProcessedAsCompositeNode(node)) {
116                 return this;
117             }
118
119             if (wasProcessAsSimpleNode(node)) {
120                 return this;
121             }
122         }
123
124         throw new IllegalStateException("It wasn't possible to serialize node " + node);
125     }
126
127     private void write(final List<NormalizedNode> nodes, final SchemaNode dataSchemaNode) throws IOException {
128         for (final NormalizedNode node : nodes) {
129             write(node, dataSchemaNode);
130         }
131     }
132
133     @Override
134     protected boolean writeChildren(final Iterable<? extends NormalizedNode> children) throws IOException {
135         return writeChildren(children, currentSchemaNode, true);
136     }
137
138     private boolean writeChildren(final Iterable<? extends NormalizedNode> children, final SchemaNode parentSchemaNode,
139             final boolean endParent) throws IOException {
140         //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
141         final ArrayListMultimap<QName, NormalizedNode> qNameToNodes = ArrayListMultimap.create();
142         for (final NormalizedNode child : children) {
143             if (child instanceof AugmentationNode) {
144                 qNameToNodes.putAll(resolveAugmentations((AugmentationNode) child));
145             } else {
146                 qNameToNodes.put(child.getNodeType(), child);
147             }
148         }
149
150         if (parentSchemaNode instanceof DataNodeContainer) {
151             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
152                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
153             } else {
154                 for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
155                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
156                 }
157             }
158         } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
159             for (final CaseSchemaNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
160                 for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
161                     if (qNameToNodes.containsKey(dsn.getQName())) {
162                         write(qNameToNodes.get(dsn.getQName()), dsn);
163                     }
164                 }
165             }
166         } else {
167             for (final NormalizedNode child : children) {
168                 writeLeaf(child);
169             }
170         }
171         if (endParent) {
172             getWriter().endNode();
173         }
174         return true;
175     }
176
177     private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode node) throws IOException {
178         if (wasProcessAsSimpleNode(node)) {
179             return this;
180         }
181
182         throw new IllegalStateException("It wasn't possible to serialize node " + node);
183     }
184
185     private ArrayListMultimap<QName, NormalizedNode> resolveAugmentations(final AugmentationNode child) {
186         final ArrayListMultimap<QName, NormalizedNode> resolvedAugs = ArrayListMultimap.create();
187         for (final NormalizedNode node : child.body()) {
188             if (node instanceof AugmentationNode) {
189                 resolvedAugs.putAll(resolveAugmentations((AugmentationNode) node));
190             } else {
191                 resolvedAugs.put(node.getNodeType(), node);
192             }
193         }
194         return resolvedAugs;
195     }
196
197     private final class SchemaNodeSetter implements AutoCloseable {
198
199         private final SchemaNode previousSchemaNode;
200
201         /**
202          * Sets current schema node new value and store old value for later restore.
203          */
204         SchemaNodeSetter(final SchemaNode schemaNode) {
205             previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode;
206             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode;
207         }
208
209         /**
210          * Restore previous schema node.
211          */
212         @Override
213         public void close() {
214             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode;
215         }
216     }
217 }