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