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