5363661c0e69518027647ef5c905188ff0d6735c
[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.Optional;
13 import com.google.common.base.Preconditions;
14 import java.io.IOException;
15 import java.net.URI;
16 import java.util.LinkedList;
17 import java.util.List;
18 import javax.xml.transform.dom.DOMSource;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
22 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
26 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
28 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
29 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding
36  * schema element in the passed in SchemaContext.
37  */
38 public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
39     private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodePruner.class);
40
41     public static final URI BASE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
42     private final SimpleStack<NormalizedNodeBuilderWrapper> stack = new SimpleStack<>();
43     private NormalizedNode<?,?> normalizedNode;
44     private final DataSchemaContextNode<?> nodePathSchemaNode;
45     private boolean sealed = false;
46
47     public NormalizedNodePruner(YangInstanceIdentifier nodePath, SchemaContext schemaContext) {
48         nodePathSchemaNode = findSchemaNodeForNodePath(nodePath, schemaContext);
49     }
50
51     @SuppressWarnings("unchecked")
52     @Override
53     public void leafNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, Object value)
54             throws IOException, IllegalArgumentException {
55
56         checkNotSealed();
57
58         NormalizedNodeBuilderWrapper parent = stack.peek();
59         LeafNode<Object> leafNode = Builders.leafBuilder().withNodeIdentifier(nodeIdentifier).withValue(value).build();
60         if (parent != null) {
61             if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
62                 parent.builder().addChild(leafNode);
63             }
64         } else {
65             // If there's no parent node then this is a stand alone LeafNode.
66             if (nodePathSchemaNode != null) {
67                 this.normalizedNode = leafNode;
68             }
69
70             sealed = true;
71         }
72     }
73
74     @Override
75     public void startLeafSet(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
76             throws IOException, IllegalArgumentException {
77         checkNotSealed();
78
79         addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
80     }
81
82     @Override
83     public void startOrderedLeafSet(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int str)
84             throws IOException, IllegalArgumentException {
85         checkNotSealed();
86
87         addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
88     }
89
90     @SuppressWarnings({ "unchecked" })
91     @Override
92     public void leafSetEntryNode(QName name, Object value) throws IOException, IllegalArgumentException {
93         checkNotSealed();
94
95         NormalizedNodeBuilderWrapper parent = stack.peek();
96         if (parent != null) {
97             if (hasValidSchema(name, parent)) {
98                 parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(value)
99                         .withNodeIdentifier(new YangInstanceIdentifier.NodeWithValue<>(parent.nodeType(), value))
100                         .build());
101             }
102         } else {
103             // If there's no parent LeafSetNode then this is a stand alone
104             // LeafSetEntryNode.
105             if (nodePathSchemaNode != null) {
106                 this.normalizedNode = Builders.leafSetEntryBuilder().withValue(value).withNodeIdentifier(
107                         new YangInstanceIdentifier.NodeWithValue<>(name, value)).build();
108             }
109
110             sealed = true;
111         }
112     }
113
114     @Override
115     public void startContainerNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
116             throws IOException, IllegalArgumentException {
117         checkNotSealed();
118
119         addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
120     }
121
122     @Override
123     public void startYangModeledAnyXmlNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
124             throws IOException, IllegalArgumentException {
125         throw new UnsupportedOperationException("Not implemented yet");
126     }
127
128     @Override
129     public void startUnkeyedList(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
130             throws IOException, IllegalArgumentException {
131         checkNotSealed();
132
133         addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
134     }
135
136     @Override
137     public void startUnkeyedListItem(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
138             throws IOException, IllegalStateException {
139         checkNotSealed();
140
141         addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
142     }
143
144     @Override
145     public void startMapNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
146             throws IOException, IllegalArgumentException {
147         checkNotSealed();
148
149         addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
150     }
151
152     @Override
153     public void startMapEntryNode(YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates,
154             int count)  throws IOException, IllegalArgumentException {
155         checkNotSealed();
156
157         addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates),
158                 nodeIdentifierWithPredicates);
159     }
160
161     @Override
162     public void startOrderedMapNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
163             throws IOException, IllegalArgumentException {
164         checkNotSealed();
165
166         addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
167     }
168
169     @Override
170     public void startChoiceNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, int count)
171             throws IOException, IllegalArgumentException {
172         checkNotSealed();
173
174         addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
175     }
176
177     @Override
178     public void startAugmentationNode(YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier)
179             throws IOException, IllegalArgumentException {
180
181         checkNotSealed();
182
183         addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier);
184     }
185
186     @SuppressWarnings("unchecked")
187     @Override
188     public void anyxmlNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, Object value)
189             throws IOException, IllegalArgumentException {
190         checkNotSealed();
191
192         NormalizedNodeBuilderWrapper parent = stack.peek();
193         AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) value)
194                 .build();
195         if (parent != null) {
196             if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
197                 parent.builder().addChild(anyXmlNode);
198             }
199         } else {
200             // If there's no parent node then this is a stand alone AnyXmlNode.
201             if (nodePathSchemaNode != null) {
202                 this.normalizedNode = anyXmlNode;
203             }
204
205             sealed = true;
206         }
207     }
208
209     @SuppressWarnings("unchecked")
210     @Override
211     public void endNode() throws IOException, IllegalStateException {
212         checkNotSealed();
213
214         NormalizedNodeBuilderWrapper child = stack.pop();
215
216         Preconditions.checkState(child != null, "endNode called on an empty stack");
217
218         if (!child.getSchema().isPresent()) {
219             LOG.debug("Schema not found for {}", child.identifier());
220             return;
221         }
222
223         NormalizedNode<?,?> newNode = child.builder().build();
224
225         if (stack.size() > 0) {
226             NormalizedNodeBuilderWrapper parent = stack.peek();
227             parent.builder().addChild(newNode);
228         } else {
229             this.normalizedNode = newNode;
230             sealed = true;
231         }
232     }
233
234     @Override
235     public void close() throws IOException {
236         sealed = true;
237     }
238
239     @Override
240     public void flush() throws IOException {
241
242     }
243
244     public NormalizedNode<?,?> normalizedNode() {
245         return normalizedNode;
246     }
247
248     private void checkNotSealed() {
249         Preconditions.checkState(!sealed, "Pruner can be used only once");
250     }
251
252     private static boolean hasValidSchema(QName name, NormalizedNodeBuilderWrapper parent) {
253         boolean valid = parent.getSchema().isPresent() && parent.getSchema().get().getChild(name) != null;
254         if (!valid) {
255             LOG.debug("Schema not found for {}", name);
256         }
257
258         return valid;
259     }
260
261     private NormalizedNodeBuilderWrapper addBuilder(NormalizedNodeContainerBuilder<?,?,?,?> builder,
262             PathArgument identifier) {
263         final Optional<DataSchemaContextNode<?>> schemaNode;
264         NormalizedNodeBuilderWrapper parent = stack.peek();
265         if (parent == null) {
266             schemaNode = Optional.fromNullable(nodePathSchemaNode);
267         } else if (parent.getSchema().isPresent()) {
268             schemaNode = Optional.fromNullable(parent.getSchema().get().getChild(identifier));
269         } else {
270             schemaNode = Optional.absent();
271         }
272
273         NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode);
274         stack.push(wrapper);
275         return wrapper;
276     }
277
278     private static DataSchemaContextNode<?> findSchemaNodeForNodePath(YangInstanceIdentifier nodePath,
279             SchemaContext schemaContext) {
280         DataSchemaContextNode<?> schemaNode = DataSchemaContextTree.from(schemaContext).getRoot();
281         for (PathArgument arg : nodePath.getPathArguments()) {
282             schemaNode = schemaNode.getChild(arg);
283             if (schemaNode == null) {
284                 break;
285             }
286         }
287
288         return schemaNode;
289     }
290
291     @VisibleForTesting
292     static class SimpleStack<E> {
293         List<E> stack = new LinkedList<>();
294
295         void push(E element) {
296             stack.add(element);
297         }
298
299         E pop() {
300             if (size() == 0) {
301                 return null;
302             }
303             return stack.remove(stack.size() - 1);
304         }
305
306         E peek() {
307             if (size() == 0) {
308                 return null;
309             }
310
311             return stack.get(stack.size() - 1);
312         }
313
314         int size() {
315             return stack.size();
316         }
317     }
318 }