Do not use Optional in NormalizedNodeWrapper
[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 = findSchemaNodeForNodePath(nodePath, schemaContext);
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         checkNotSealed();
79         addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
80     }
81
82     @Override
83     public void startOrderedLeafSet(final NodeIdentifier nodeIdentifier, final int str) {
84         checkNotSealed();
85         addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
86     }
87
88     @SuppressWarnings("unchecked")
89     @Override
90     public void leafSetEntryNode(final QName name, final Object value) {
91         checkNotSealed();
92
93         NormalizedNodeBuilderWrapper parent = stack.peek();
94         if (parent != null) {
95             if (hasValidSchema(name, parent)) {
96                 parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(value)
97                         .withNodeIdentifier(new NodeWithValue<>(parent.nodeType(), value))
98                         .build());
99             }
100         } else {
101             // If there's no parent LeafSetNode then this is a stand alone
102             // LeafSetEntryNode.
103             if (nodePathSchemaNode != null) {
104                 this.normalizedNode = Builders.leafSetEntryBuilder().withValue(value).withNodeIdentifier(
105                         new NodeWithValue<>(name, value)).build();
106             }
107
108             sealed = true;
109         }
110     }
111
112     @Override
113     public void startContainerNode(final NodeIdentifier nodeIdentifier, final int count) {
114         checkNotSealed();
115         addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
116     }
117
118     @Override
119     public void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) {
120         throw new UnsupportedOperationException("Not implemented yet");
121     }
122
123     @Override
124     public void startUnkeyedList(final NodeIdentifier nodeIdentifier, final int count) {
125         checkNotSealed();
126         addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
127     }
128
129     @Override
130     public void startUnkeyedListItem(final NodeIdentifier nodeIdentifier, final int count) {
131         checkNotSealed();
132         addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
133     }
134
135     @Override
136     public void startMapNode(final NodeIdentifier nodeIdentifier, final int count) {
137         checkNotSealed();
138         addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
139     }
140
141     @Override
142     public void startMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifierWithPredicates, final int count) {
143         checkNotSealed();
144         addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates),
145                 nodeIdentifierWithPredicates);
146     }
147
148     @Override
149     public void startOrderedMapNode(final NodeIdentifier nodeIdentifier, final int count) {
150         checkNotSealed();
151         addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
152     }
153
154     @Override
155     public void startChoiceNode(final NodeIdentifier nodeIdentifier, final int count) {
156         checkNotSealed();
157         addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
158     }
159
160     @Override
161     public void startAugmentationNode(final AugmentationIdentifier augmentationIdentifier) {
162         checkNotSealed();
163         addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier);
164     }
165
166     @SuppressWarnings("unchecked")
167     @Override
168     public void anyxmlNode(final NodeIdentifier nodeIdentifier, final Object value) {
169         checkNotSealed();
170
171         NormalizedNodeBuilderWrapper parent = stack.peek();
172         AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) value)
173                 .build();
174         if (parent != null) {
175             if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
176                 parent.builder().addChild(anyXmlNode);
177             }
178         } else {
179             // If there's no parent node then this is a stand alone AnyXmlNode.
180             if (nodePathSchemaNode != null) {
181                 this.normalizedNode = anyXmlNode;
182             }
183
184             sealed = true;
185         }
186     }
187
188     @SuppressWarnings("unchecked")
189     @Override
190     public void endNode() {
191         checkNotSealed();
192
193         final NormalizedNodeBuilderWrapper child;
194         try {
195             child = stack.pop();
196         } catch (NoSuchElementException e) {
197             throw new IllegalStateException("endNode called on an empty stack", e);
198         }
199
200         if (child.getSchema() == null) {
201             LOG.debug("Schema not found for {}", child.identifier());
202             return;
203         }
204
205         NormalizedNode<?, ?> newNode = child.builder().build();
206         if (stack.size() > 0) {
207             NormalizedNodeBuilderWrapper parent = stack.peek();
208             parent.builder().addChild(newNode);
209         } else {
210             this.normalizedNode = newNode;
211             sealed = true;
212         }
213     }
214
215     @Override
216     public void close() {
217         sealed = true;
218     }
219
220     @Override
221     public void flush() {
222         // No-op
223     }
224
225     public NormalizedNode<?, ?> normalizedNode() {
226         return normalizedNode;
227     }
228
229     private void checkNotSealed() {
230         checkState(!sealed, "Pruner can be used only once");
231     }
232
233     private static boolean hasValidSchema(final QName name, final NormalizedNodeBuilderWrapper parent) {
234         final DataSchemaContextNode<?> parentSchema = parent.getSchema();
235         final boolean valid = parentSchema != null && parentSchema.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 DataSchemaContextNode<?> schemaNode;
246         final NormalizedNodeBuilderWrapper parent = stack.peek();
247         if (parent != null) {
248             final DataSchemaContextNode<?> parentSchema = parent.getSchema();
249             schemaNode = parentSchema == null ? null : parentSchema.getChild(identifier);
250         } else {
251             schemaNode = nodePathSchemaNode;
252         }
253
254         NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode);
255         stack.push(wrapper);
256         return wrapper;
257     }
258
259     private static DataSchemaContextNode<?> findSchemaNodeForNodePath(final YangInstanceIdentifier nodePath,
260             final SchemaContext schemaContext) {
261         DataSchemaContextNode<?> schemaNode = DataSchemaContextTree.from(schemaContext).getRoot();
262         for (PathArgument arg : nodePath.getPathArguments()) {
263             schemaNode = schemaNode.getChild(arg);
264             if (schemaNode == null) {
265                 break;
266             }
267         }
268
269         return schemaNode;
270     }
271 }