ecbaf1bf582faa454456ed6563cbc411ee4dfc9c
[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.base.Optional;
13 import java.net.URI;
14 import java.util.ArrayDeque;
15 import java.util.Deque;
16 import java.util.NoSuchElementException;
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 Deque<NormalizedNodeBuilderWrapper> stack = new ArrayDeque<>();
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         final NormalizedNodeBuilderWrapper child;
195         try {
196             child = stack.pop();
197         } catch (NoSuchElementException e) {
198             throw new IllegalStateException("endNode called on an empty stack", e);
199         }
200
201         if (!child.getSchema().isPresent()) {
202             LOG.debug("Schema not found for {}", child.identifier());
203             return;
204         }
205
206         NormalizedNode<?, ?> newNode = child.builder().build();
207         if (stack.size() > 0) {
208             NormalizedNodeBuilderWrapper parent = stack.peek();
209             parent.builder().addChild(newNode);
210         } else {
211             this.normalizedNode = newNode;
212             sealed = true;
213         }
214     }
215
216     @Override
217     public void close() {
218         sealed = true;
219     }
220
221     @Override
222     public void flush() {
223         // No-op
224     }
225
226     public NormalizedNode<?, ?> normalizedNode() {
227         return normalizedNode;
228     }
229
230     private void checkNotSealed() {
231         checkState(!sealed, "Pruner can be used only once");
232     }
233
234     private static boolean hasValidSchema(final QName name, final NormalizedNodeBuilderWrapper parent) {
235         boolean valid = parent.getSchema().isPresent() && parent.getSchema().get().getChild(name) != null;
236         if (!valid) {
237             LOG.debug("Schema not found for {}", name);
238         }
239
240         return valid;
241     }
242
243     private NormalizedNodeBuilderWrapper addBuilder(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder,
244             final PathArgument identifier) {
245         final Optional<DataSchemaContextNode<?>> schemaNode;
246         NormalizedNodeBuilderWrapper parent = stack.peek();
247         if (parent == null) {
248             schemaNode = Optional.fromNullable(nodePathSchemaNode);
249         } else if (parent.getSchema().isPresent()) {
250             schemaNode = Optional.fromNullable(parent.getSchema().get().getChild(identifier));
251         } else {
252             schemaNode = Optional.absent();
253         }
254
255         NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode);
256         stack.push(wrapper);
257         return wrapper;
258     }
259
260     private static DataSchemaContextNode<?> findSchemaNodeForNodePath(final YangInstanceIdentifier nodePath,
261             final SchemaContext schemaContext) {
262         DataSchemaContextNode<?> schemaNode = DataSchemaContextTree.from(schemaContext).getRoot();
263         for (PathArgument arg : nodePath.getPathArguments()) {
264             schemaNode = schemaNode.getChild(arg);
265             if (schemaNode == null) {
266                 break;
267             }
268         }
269
270         return schemaNode;
271     }
272 }