037265f94f00e4764e5d082bf4df4cb2541638f3
[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.collect.ArrayListMultimap;
11 import java.io.IOException;
12 import java.util.Collection;
13 import java.util.List;
14 import java.util.Objects;
15 import org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
20 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
29
30 /**
31  * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter},
32  * this iterates over elements in order as they are defined in .yang file.
33  */
34 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
35
36     private final SchemaContext schemaContext;
37     private final SchemaNode root;
38     private final NormalizedNodeStreamWriter writer;
39
40     private SchemaNode currentSchemaNode;
41
42     /**
43      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
44      * @param writer Back-end writer
45      * @param schemaContext Schema context
46      * @param path path
47      */
48     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaPath path) {
49         super(writer);
50         this.writer = writer;
51         this.schemaContext = schemaContext;
52         this.root = SchemaUtils.findParentSchemaOnPath(schemaContext, path);
53     }
54
55     @Override
56     public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
57         if (Objects.equals(root, schemaContext)) {
58             currentSchemaNode = schemaContext.getDataChildByName(node.getNodeType());
59         } else {
60             currentSchemaNode = root;
61         }
62         return write(node, currentSchemaNode);
63
64     }
65
66     /**
67      * Iterate over the provided collection and emit write
68      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
69      *
70      * @param nodes nodes
71      * @return NormalizedNodeWriter this
72      * @throws IOException when thrown from the backing writer.
73      */
74     public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
75         currentSchemaNode = root;
76         if (writeChildren(nodes, currentSchemaNode, false)) {
77             return this;
78         }
79
80         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
81
82     }
83
84     private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException {
85
86         //Set current schemaNode
87         try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
88             if (node == null) {
89                 return this;
90             }
91
92             if (wasProcessedAsCompositeNode(node)) {
93                 return this;
94             }
95
96             if (wasProcessAsSimpleNode(node)) {
97                 return this;
98             }
99         }
100
101         throw new IllegalStateException("It wasn't possible to serialize node " + node);
102     }
103
104     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
105         for (NormalizedNode<?, ?> node : nodes) {
106             write(node, dataSchemaNode);
107         }
108     }
109
110     protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
111         return writeChildren(children, currentSchemaNode, true);
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 SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
153         if (wasProcessAsSimpleNode(node)) {
154             return this;
155         }
156
157         throw new IllegalStateException("It wasn't possible to serialize node " + node);
158     }
159
160     private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
161         final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
162         for (NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
163             if (node instanceof AugmentationNode) {
164                 resolvedAugs.putAll(resolveAugmentations(node));
165             } else {
166                 resolvedAugs.put(node.getNodeType(), node);
167             }
168         }
169         return resolvedAugs;
170     }
171
172
173     private class SchemaNodeSetter implements AutoCloseable {
174
175         private final SchemaNode previousSchemaNode;
176
177         /**
178          * Sets current schema node new value and store old value for later restore
179          */
180         public SchemaNodeSetter(final SchemaNode schemaNode) {
181             previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode;
182             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode;
183         }
184
185         /**
186          * Restore previous schema node
187          */
188         @Override
189         public void close() {
190             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode;
191         }
192     }
193
194 }