Remove NormalizedNodePruner
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / transformer / AbstractNormalizedNodePruner.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 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import java.io.IOException;
15 import java.util.ArrayDeque;
16 import java.util.Deque;
17 import java.util.NoSuchElementException;
18 import java.util.Optional;
19 import javax.xml.transform.dom.DOMSource;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
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.ReusableImmutableNormalizedNodeStreamWriter;
29 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
30 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
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 abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWriter {
41     enum State {
42         UNITIALIZED,
43         OPEN,
44         CLOSED;
45     }
46
47     @FunctionalInterface
48     interface WriterMethod<T extends PathArgument> {
49
50         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name) throws IOException;
51     }
52
53     @FunctionalInterface
54     interface SizedWriterMethod<T extends PathArgument> {
55
56         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name, int childSizeHint) throws IOException;
57     }
58
59     private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodePruner.class);
60
61     private final Deque<DataSchemaContextNode<?>> stack = new ArrayDeque<>();
62     private final ReusableImmutableNormalizedNodeStreamWriter delegate =
63             ReusableImmutableNormalizedNodeStreamWriter.create();
64     private final DataSchemaContextTree tree;
65
66     private DataSchemaContextNode<?> nodePathSchemaNode;
67     private NormalizedNode<?, ?> normalizedNode;
68     private State state = State.UNITIALIZED;
69     private int unknown;
70
71     AbstractNormalizedNodePruner(final DataSchemaContextTree tree) {
72         this.tree = requireNonNull(tree);
73     }
74
75     AbstractNormalizedNodePruner(final SchemaContext schemaContext) {
76         this(DataSchemaContextTree.from(schemaContext));
77     }
78
79     final DataSchemaContextTree getTree() {
80         return tree;
81     }
82
83     final void initialize(final YangInstanceIdentifier nodePath) {
84         nodePathSchemaNode = tree.findChild(nodePath).orElse(null);
85         unknown = 0;
86         normalizedNode = null;
87         stack.clear();
88         delegate.reset();
89         state = State.OPEN;
90     }
91
92     @Override
93     public final void startLeafNode(final NodeIdentifier name) throws IOException {
94         enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafNode, name);
95     }
96
97     @Override
98     public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
99         enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafSet, name, childSizeHint);
100     }
101
102     @Override
103     public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
104         enter(ReusableImmutableNormalizedNodeStreamWriter::startOrderedLeafSet, name, childSizeHint);
105     }
106
107     @Override
108     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
109         enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafSetEntryNode, name);
110     }
111
112     @Override
113     public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
114         enter(ReusableImmutableNormalizedNodeStreamWriter::startContainerNode, name, childSizeHint);
115     }
116
117     @Override
118     public final void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) {
119         // FIXME: implement this
120         throw new UnsupportedOperationException("Not implemented yet");
121     }
122
123     @Override
124     public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
125         enter(ReusableImmutableNormalizedNodeStreamWriter::startUnkeyedList, name, childSizeHint);
126     }
127
128     @Override
129     public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
130         enter(ReusableImmutableNormalizedNodeStreamWriter::startUnkeyedListItem, name, childSizeHint);
131     }
132
133     @Override
134     public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
135         enter(ReusableImmutableNormalizedNodeStreamWriter::startMapNode, name, childSizeHint);
136     }
137
138     @Override
139     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
140             throws IOException {
141         enter(ReusableImmutableNormalizedNodeStreamWriter::startMapEntryNode, identifier, childSizeHint);
142     }
143
144     @Override
145     public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
146         enter(ReusableImmutableNormalizedNodeStreamWriter::startOrderedMapNode, name, childSizeHint);
147     }
148
149     @Override
150     public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
151         enter(ReusableImmutableNormalizedNodeStreamWriter::startChoiceNode, name, childSizeHint);
152     }
153
154     @Override
155     public final void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
156         enter(ReusableImmutableNormalizedNodeStreamWriter::startAugmentationNode, identifier);
157     }
158
159     @Override
160     public final  boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
161         if (enter(name)) {
162             verify(delegate.startAnyxmlNode(name, objectModel),
163                 "Unexpected failure to stream DOMSource node %s model %s", name, objectModel);
164         }
165         return true;
166     }
167
168     @Override
169     public final boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
170         // FIXME: we do not support anydata nodes yet
171         return false;
172     }
173
174     @Override
175     public final  void domSourceValue(final DOMSource value) throws IOException {
176         checkNotSealed();
177         if (unknown == 0) {
178             delegate.domSourceValue(value);
179         }
180     }
181
182     @Override
183     public final void scalarValue(final Object value) throws IOException {
184         checkNotSealed();
185         if (unknown == 0) {
186             delegate.scalarValue(translateScalar(currentSchema(), value));
187         }
188     }
189
190     Object translateScalar(final DataSchemaContextNode<?> context, final Object value) throws IOException {
191         // Default is pass-through
192         return value;
193     }
194
195     @Override
196     public final void endNode() throws IOException {
197         checkNotSealed();
198
199         if (unknown == 0) {
200             try {
201                 stack.pop();
202             } catch (NoSuchElementException e) {
203                 throw new IllegalStateException("endNode called on an empty stack", e);
204             }
205             delegate.endNode();
206         } else {
207             unknown--;
208             if (unknown != 0) {
209                 // Still at unknown, do not attempt to create result
210                 return;
211             }
212         }
213
214         if (stack.isEmpty()) {
215             normalizedNode = delegate.getResult();
216             state = State.CLOSED;
217         }
218     }
219
220     @Override
221     public final void close() throws IOException {
222         state = State.CLOSED;
223         stack.clear();
224         delegate.close();
225     }
226
227     @Override
228     public final void flush() throws IOException {
229         delegate.flush();
230     }
231
232     /**
233      * Return the resulting normalized node.
234      *
235      * @return Resulting node for the path, if it was not pruned
236      * @throws IllegalStateException if this pruner has not been closed
237      */
238     public final Optional<NormalizedNode<?, ?>> getResult() {
239         checkState(state == State.CLOSED, "Cannot get result in state %s", state);
240         return Optional.ofNullable(normalizedNode);
241     }
242
243     private void checkNotSealed() {
244         checkState(state == State.OPEN, "Illegal operation in state %s", state);
245     }
246
247     private boolean enter(final PathArgument name) {
248         checkNotSealed();
249
250         if (unknown != 0) {
251             LOG.debug("Skipping child {} in unknown subtree", name);
252             unknown++;
253             return false;
254         }
255
256         final DataSchemaContextNode<?> schema;
257         final DataSchemaContextNode<?> parent = currentSchema();
258         if (parent != null) {
259             schema = parent.getChild(name);
260         } else {
261             schema = nodePathSchemaNode;
262         }
263
264         if (schema == null) {
265             LOG.debug("Schema not found for {}", name);
266             unknown = 1;
267             return false;
268         }
269
270         stack.push(schema);
271         final DataSchemaNode dataSchema = schema.getDataSchemaNode();
272         if (dataSchema != null) {
273             delegate.nextDataSchemaNode(dataSchema);
274         }
275         return true;
276     }
277
278     final <A extends PathArgument> void enter(final WriterMethod<A> method, final A name) throws IOException {
279         if (enter(name)) {
280             method.apply(delegate, name);
281         }
282     }
283
284     final <A extends PathArgument> void enter(final SizedWriterMethod<A> method, final A name, final int size)
285             throws IOException {
286         if (enter(name)) {
287             method.apply(delegate, name, size);
288         }
289     }
290
291     final DataSchemaContextNode<?> currentSchema() {
292         return stack.peek();
293     }
294 }