2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.impl.schema;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
14 import java.io.IOException;
15 import java.util.ArrayDeque;
16 import java.util.Deque;
17 import javax.xml.transform.dom.DOMSource;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
27 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
28 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
29 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserLeafSetNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserMapNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.spi.node.InterningLeafNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.util.LeafInterner;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
41 * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
42 * {@link NormalizedNode}s.
45 * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
46 * NormalizedNode will be stored in supplied result object.
49 * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
50 * written to this builder.
53 * This class is not final for purposes of customization, normal users should not need to subclass it.
55 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
56 private final Deque<NormalizedNode.Builder> builders = new ArrayDeque<>();
58 private DataSchemaNode nextSchema;
60 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNode.Builder topLevelBuilder) {
61 builders.push(topLevelBuilder);
64 protected ImmutableNormalizedNodeStreamWriter(final NormalizationResultHolder holder) {
65 this(new NormalizationResultBuilder(holder));
69 * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
70 * and writes them to supplied builder as child nodes.
73 * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
74 * to ensure proper construction of data.
76 * @param builder Builder to which data will be written.
77 * @return {@link NormalizedNodeStreamWriter} which writes data
79 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
80 return new ImmutableNormalizedNodeStreamWriter(builder);
84 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
85 * (type of NormalizedNode) is determined by first start event.
88 * Result is built when {@link #endNode()} associated with that start event is emitted.
91 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
95 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
96 * type of {@link NormalizedNode} will be written.
98 * @param holder {@link NormalizationResultHolder} object which will hold result value.
99 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
101 public static @NonNull NormalizedNodeStreamWriter from(final NormalizationResultHolder holder) {
102 return new ImmutableMetadataNormalizedNodeStreamWriter(holder);
106 public void startLeafNode(final NodeIdentifier name) {
107 checkDataNodeContainer();
108 enter(name, leafNodeBuilder(nextSchema));
113 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
114 checkDataNodeContainer();
115 enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
116 : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
120 public void startLeafSetEntryNode(final NodeWithValue<?> name) {
121 final var current = current();
122 checkArgument(current instanceof ImmutableLeafSetNodeBuilder
123 || current instanceof ImmutableUserLeafSetNodeBuilder || current instanceof NormalizationResultBuilder,
124 "LeafSetEntryNode is not valid for parent %s", current);
125 enter(name, leafsetEntryNodeBuilder());
130 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
131 checkDataNodeContainer();
132 enter(name, Builders.orderedLeafSetBuilder());
136 public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
137 checkDataNodeContainer();
138 if (DOMSource.class.isAssignableFrom(objectModel)) {
139 enter(name, Builders.anyXmlBuilder());
147 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
148 checkDataNodeContainer();
149 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.containerBuilder()
150 : Builders.containerBuilder(childSizeHint));
154 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
155 checkDataNodeContainer();
156 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.unkeyedListBuilder()
157 : Builders.unkeyedListBuilder(childSizeHint));
161 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
162 final var current = current();
163 checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
164 || current instanceof NormalizationResultBuilder);
165 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.unkeyedListEntryBuilder()
166 : Builders.unkeyedListEntryBuilder(childSizeHint));
170 public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
171 checkDataNodeContainer();
172 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.mapBuilder() : Builders.mapBuilder(childSizeHint));
176 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
177 final var current = current();
178 checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableUserMapNodeBuilder
179 || current instanceof NormalizationResultBuilder);
181 enter(identifier, UNKNOWN_SIZE == childSizeHint ? Builders.mapEntryBuilder()
182 : Builders.mapEntryBuilder(childSizeHint));
186 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
187 checkDataNodeContainer();
188 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.orderedMapBuilder()
189 : Builders.orderedMapBuilder(childSizeHint));
193 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
194 checkDataNodeContainer();
195 enter(name, UNKNOWN_SIZE == childSizeHint ? Builders.choiceBuilder() : Builders.choiceBuilder(childSizeHint));
199 public void flush() {
204 public void close() {
209 public void nextDataSchemaNode(final DataSchemaNode schema) {
210 nextSchema = requireNonNull(schema);
214 public void scalarValue(final Object value) {
215 // FIXME: tighten to concrete NormalizedNode.Builder interfaces
216 currentScalar().withValue(value);
220 public void domSourceValue(final DOMSource value) {
221 // FIXME: tighten to concrete NormalizedNode.Builder interfaces
222 currentScalar().withValue(value);
226 @SuppressWarnings("rawtypes")
227 public void endNode() {
228 final var finishedBuilder = builders.poll();
229 checkState(finishedBuilder != null, "Node which should be closed does not exists.");
230 final var product = finishedBuilder.build();
237 public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
238 checkDataNodeContainer();
239 enter(name, Builders.anydataBuilder(objectModel));
240 // We support all object models
245 * Add a child not to the currently-open builder.
247 * @param child A new child
248 * @throws NullPointerException if {@code child} is null
249 * @throws IllegalStateException if there is no open builder
251 @SuppressWarnings({ "rawtypes", "unchecked" })
252 protected final void writeChild(final NormalizedNode child) {
253 final NormalizedNodeContainerBuilder current = currentContainer();
254 checkState(current != null, "Reached top level node, which could not be closed in this writer.");
255 current.addChild(requireNonNull(child));
258 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
259 @SuppressWarnings("rawtypes")
260 void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
261 builders.push(next.withNodeIdentifier(identifier));
265 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
266 protected final NormalizedNode.Builder popBuilder() {
267 return builders.pop();
270 final void reset(final NormalizationResultBuilder builder) {
273 builders.push(builder);
276 private <T> LeafNode.Builder<T> leafNodeBuilder(final DataSchemaNode schema) {
277 final var builder = this.<T>leafNodeBuilder();
278 if (schema instanceof LeafSchemaNode leafSchema) {
279 final var interner = LeafInterner.<LeafNode<T>>forSchema(leafSchema);
280 if (interner.isPresent()) {
281 return new InterningLeafNodeBuilder<>(builder, interner.orElseThrow());
287 <T> LeafNode.@NonNull Builder<T> leafNodeBuilder() {
288 return Builders.leafBuilder();
291 <T> LeafSetEntryNode.@NonNull Builder<T> leafsetEntryNodeBuilder() {
292 return Builders.leafSetEntryBuilder();
295 private void checkDataNodeContainer() {
296 @SuppressWarnings("rawtypes")
297 final NormalizedNodeContainerBuilder current = currentContainer();
298 if (!(current instanceof NormalizationResultBuilder)) {
299 checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
303 private NormalizedNode.Builder current() {
304 return builders.peek();
307 @SuppressWarnings("rawtypes")
308 private NormalizedNodeContainerBuilder currentContainer() {
309 final var current = current();
310 if (current instanceof NormalizedNodeContainerBuilder builder) {
313 if (current != null) {
314 throw new IllegalStateException(current + " is not a node container");
319 @SuppressWarnings("rawtypes")
320 private NormalizedNodeBuilder currentScalar() {
321 final var current = current();
322 if (current instanceof NormalizedNodeContainerBuilder) {
323 throw new IllegalStateException("Unexpected node container " + current);
325 if (current instanceof NormalizedNodeBuilder builder) {
328 throw new IllegalStateException("Unexpected non-scalar " + current);