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