Bug 2062 - StreamWriter APIs loses information about leaf-set ordering
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / transformer / NormalizedNodePruner.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Preconditions;
13
14 import java.io.IOException;
15 import java.net.URI;
16 import java.util.HashSet;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Set;
20
21 import javax.xml.transform.dom.DOMSource;
22
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
27 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
28 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30
31 /**
32  * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding
33  * schema element in the passed in SchemaContext
34  *
35  */
36 public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
37
38     public static final URI BASE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
39     private final SimpleStack<NormalizedNodeBuilderWrapper> stack = new SimpleStack<>();
40     private NormalizedNode<?,?> normalizedNode;
41     private final Set<URI> validNamespaces;
42     private boolean sealed = false;
43
44     public NormalizedNodePruner(SchemaContext schemaContext) {
45         this(NormalizedNodePruner.namespaces(schemaContext));
46     }
47
48     public NormalizedNodePruner(Set<URI> validNamespaces) {
49         this.validNamespaces = validNamespaces;
50     }
51
52     @Override
53     public void leafNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, Object o) throws IOException, IllegalArgumentException {
54
55         checkNotSealed();
56
57         if(!isValidNamespace(nodeIdentifier)){
58             return;
59         }
60         NormalizedNodeBuilderWrapper parent = stack.peek();
61         Preconditions.checkState(parent != null, "leafNode has no parent");
62         parent.builder()
63                 .addChild(Builders.leafBuilder()
64                         .withNodeIdentifier(nodeIdentifier)
65                         .withValue(o)
66                         .build());
67     }
68
69     @Override
70     public void startLeafSet(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
71
72         checkNotSealed();
73
74         addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
75     }
76
77     @Override
78     public void startOrderedLeafSet(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
79
80         checkNotSealed();
81
82         addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
83     }
84
85     @Override
86     public void leafSetEntryNode(Object o) throws IOException, IllegalArgumentException {
87
88         checkNotSealed();
89
90         NormalizedNodeBuilderWrapper parent = stack.peek();
91         Preconditions.checkState(parent != null, "leafSetEntryNode has no parent");
92         if(!isValidNamespace(parent.identifier())){
93             return;
94         }
95
96         parent.builder()
97                 .addChild(Builders.leafSetEntryBuilder()
98                         .withValue(o)
99                         .withNodeIdentifier(new YangInstanceIdentifier.NodeWithValue(parent.nodeType(), o))
100                         .build());
101     }
102
103     @Override
104     public void startContainerNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
105
106         checkNotSealed();
107
108         addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
109     }
110
111     @Override
112     public void startYangModeledAnyXmlNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
113         throw new UnsupportedOperationException("Not implemented yet");
114     }
115
116     @Override
117     public void startUnkeyedList(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
118
119         checkNotSealed();
120
121         addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
122     }
123
124     @Override
125     public void startUnkeyedListItem(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalStateException {
126
127         checkNotSealed();
128
129         addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
130     }
131
132     @Override
133     public void startMapNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
134
135         checkNotSealed();
136
137         addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
138     }
139
140     @Override
141     public void startMapEntryNode(YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates, int i) throws IOException, IllegalArgumentException {
142
143         checkNotSealed();
144
145         addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates), nodeIdentifierWithPredicates);
146     }
147
148     @Override
149     public void startOrderedMapNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
150
151         checkNotSealed();
152
153         addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
154     }
155
156     @Override
157     public void startChoiceNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int i) throws IOException, IllegalArgumentException {
158
159         checkNotSealed();
160
161         addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
162     }
163
164     @Override
165     public void startAugmentationNode(YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier) throws IOException, IllegalArgumentException {
166
167         checkNotSealed();
168
169         addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier);
170     }
171
172     @Override
173     public void anyxmlNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, Object o) throws IOException, IllegalArgumentException {
174
175         checkNotSealed();
176
177         if(!isValidNamespace(nodeIdentifier)){
178             return;
179         }
180         NormalizedNodeBuilderWrapper parent = stack.peek();
181         Preconditions.checkState(parent != null, "anyxmlNode has no parent");
182         parent.builder().addChild(Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) o).build());
183     }
184
185     @Override
186     public void endNode() throws IOException, IllegalStateException {
187
188         checkNotSealed();
189
190         NormalizedNodeBuilderWrapper child = stack.pop();
191
192         Preconditions.checkState(child != null, "endNode called on an empty stack");
193
194         if(!isValidNamespace(child.identifier())){
195             return;
196         }
197         NormalizedNode<?,?> normalizedNode = child.builder().build();
198
199         if(stack.size() > 0){
200             NormalizedNodeBuilderWrapper parent = stack.peek();
201             parent.builder().addChild(normalizedNode);
202         } else {
203             this.normalizedNode = normalizedNode;
204             sealed = true;
205         }
206     }
207
208     @Override
209     public void close() throws IOException {
210         sealed = true;
211     }
212
213     @Override
214     public void flush() throws IOException {
215
216     }
217
218     public NormalizedNode<?,?> normalizedNode(){
219         return normalizedNode;
220     }
221
222     private void checkNotSealed(){
223         Preconditions.checkState(!sealed, "Pruner can be used only once");
224     }
225
226     private boolean isValidNamespace(QName qName){
227         return validNamespaces.contains(qName.getNamespace());
228     }
229
230     private boolean isValidNamespace(YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier){
231         Set<QName> possibleChildNames = augmentationIdentifier.getPossibleChildNames();
232
233         for(QName qName : possibleChildNames){
234             if(isValidNamespace(qName)){
235                 return true;
236             }
237         }
238         return false;
239
240     }
241
242     private boolean isValidNamespace(YangInstanceIdentifier.PathArgument identifier){
243         if(identifier instanceof YangInstanceIdentifier.AugmentationIdentifier){
244             return isValidNamespace((YangInstanceIdentifier.AugmentationIdentifier) identifier);
245         }
246
247         return isValidNamespace(identifier.getNodeType());
248     }
249
250     private NormalizedNodeBuilderWrapper addBuilder(NormalizedNodeContainerBuilder<?,?,?,?> builder, YangInstanceIdentifier.PathArgument identifier){
251         NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier);
252         stack.push(wrapper);
253         return wrapper;
254     }
255
256     @VisibleForTesting
257     static class SimpleStack<E> {
258         List<E> stack = new LinkedList<>();
259
260         void push(E element){
261             stack.add(element);
262         }
263
264         E pop(){
265             if(size() == 0){
266                 return null;
267             }
268             return stack.remove(stack.size() - 1);
269         }
270
271         E peek(){
272             if(size() == 0){
273                 return null;
274             }
275
276             return stack.get(stack.size() - 1);
277         }
278
279         int size(){
280             return stack.size();
281         }
282     }
283
284     @VisibleForTesting
285     SimpleStack<NormalizedNodeBuilderWrapper> stack(){
286         return stack;
287     }
288
289     public static Set<URI> namespaces(SchemaContext schemaContext){
290         Set<URI> namespaces = new HashSet<>(schemaContext.getModules().size());
291         namespaces.add(BASE_NAMESPACE);
292         for(org.opendaylight.yangtools.yang.model.api.Module module : schemaContext.getModules()){
293             namespaces.add(module.getNamespace());
294         }
295         return namespaces;
296     }
297 }