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