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