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