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