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 com.google.common.collect.Interner;
15 import com.google.common.collect.Interners;
16 import java.io.IOException;
17 import java.util.ArrayDeque;
18 import java.util.Deque;
19 import javax.xml.transform.dom.DOMSource;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.eclipse.jdt.annotation.Nullable;
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.LeafNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode.BuilderFactory;
30 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
37 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
38 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
39 import org.opendaylight.yangtools.yang.data.spi.node.InterningLeafNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.spi.node.InterningLeafSetNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.util.LeafInterner;
42 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
50 * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
51 * {@link NormalizedNode}s.
54 * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
55 * NormalizedNode will be stored in supplied result object.
58 * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
59 * written to this builder.
62 * This class is not final for purposes of customization, normal users should not need to subclass it.
64 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
65 private static final Interner<LeafSetEntryNode<?>> ENTRY_INTERNER = Interners.newWeakInterner();
66 private static final BuilderFactory BUILDER_FACTORY = ImmutableNodes.builderFactory();
68 private final Deque<NormalizedNode.Builder> builders = new ArrayDeque<>();
70 private DataSchemaNode nextSchema;
72 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNode.Builder topLevelBuilder) {
73 builders.push(topLevelBuilder);
76 protected ImmutableNormalizedNodeStreamWriter(final NormalizationResultHolder holder) {
77 this(new NormalizationResultBuilder(holder));
81 * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
82 * and writes them to supplied builder as child nodes.
85 * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
86 * to ensure proper construction of data.
88 * @param builder Builder to which data will be written.
89 * @return {@link NormalizedNodeStreamWriter} which writes data
91 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
92 return new ImmutableNormalizedNodeStreamWriter(builder);
96 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
97 * (type of NormalizedNode) is determined by first start event.
100 * Result is built when {@link #endNode()} associated with that start event is emitted.
103 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
104 * of the first event.
107 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
108 * type of {@link NormalizedNode} will be written.
110 * @param holder {@link NormalizationResultHolder} object which will hold result value.
111 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
113 public static @NonNull NormalizedNodeStreamWriter from(final NormalizationResultHolder holder) {
114 return new ImmutableMetadataNormalizedNodeStreamWriter(holder);
118 public void startLeafNode(final NodeIdentifier name) {
119 checkDataNodeContainer();
120 enter(name, leafNodeBuilder(nextSchema));
125 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
126 checkDataNodeContainer();
127 final var builder = UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newSystemLeafSetBuilder()
128 : BUILDER_FACTORY.newSystemLeafSetBuilder(childSizeHint);
129 enter(name, leafSetNodeBuilder(builder, nextSchema));
132 private static <T> SystemLeafSetNode.Builder<T> leafSetNodeBuilder(final SystemLeafSetNode.Builder<T> delegate,
133 final @Nullable DataSchemaNode schema) {
134 if (schema instanceof LeafListSchemaNode leafListSchema) {
135 final var type = leafListSchema.getType();
136 if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition
137 || type instanceof IdentityrefTypeDefinition) {
138 return new InterningLeafSetNodeBuilder<T>(delegate, (Interner) ENTRY_INTERNER);
145 public void startLeafSetEntryNode(final NodeWithValue<?> name) {
146 final var current = current();
147 checkArgument(current instanceof SystemLeafSetNode.Builder
148 || current instanceof UserLeafSetNode.Builder || current instanceof NormalizationResultBuilder,
149 "LeafSetEntryNode is not valid for parent %s", current);
150 enter(name, leafsetEntryNodeBuilder());
155 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
156 checkDataNodeContainer();
157 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newUserLeafSetBuilder()
158 : BUILDER_FACTORY.newUserLeafSetBuilder(childSizeHint));
162 public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
163 checkDataNodeContainer();
164 if (DOMSource.class.isAssignableFrom(objectModel)) {
165 enter(name, BUILDER_FACTORY.newAnyxmlBuilder(DOMSource.class));
173 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
174 checkDataNodeContainer();
175 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newContainerBuilder()
176 : BUILDER_FACTORY.newContainerBuilder(childSizeHint));
180 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
181 checkDataNodeContainer();
182 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newUnkeyedListBuilder()
183 : BUILDER_FACTORY.newUnkeyedListBuilder(childSizeHint));
187 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
188 final var current = current();
189 checkArgument(current instanceof UnkeyedListNode.Builder || current instanceof NormalizationResultBuilder);
190 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newUnkeyedListEntryBuilder()
191 : BUILDER_FACTORY.newUnkeyedListEntryBuilder(childSizeHint));
195 public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
196 checkDataNodeContainer();
197 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newSystemMapBuilder()
198 : BUILDER_FACTORY.newSystemMapBuilder(childSizeHint));
202 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
203 final var current = current();
204 checkArgument(current instanceof SystemMapNode.Builder || current instanceof UserMapNode.Builder
205 || current instanceof NormalizationResultBuilder);
207 enter(identifier, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newMapEntryBuilder()
208 : BUILDER_FACTORY.newMapEntryBuilder(childSizeHint));
212 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
213 checkDataNodeContainer();
214 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newUserMapBuilder()
215 : BUILDER_FACTORY.newUserMapBuilder(childSizeHint));
219 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
220 checkDataNodeContainer();
221 enter(name, UNKNOWN_SIZE == childSizeHint ? BUILDER_FACTORY.newChoiceBuilder()
222 : BUILDER_FACTORY.newChoiceBuilder(childSizeHint));
226 public void flush() {
231 public void close() {
236 public void nextDataSchemaNode(final DataSchemaNode schema) {
237 nextSchema = requireNonNull(schema);
241 public void scalarValue(final Object value) {
242 // FIXME: tighten to concrete NormalizedNode.Builder interfaces
243 currentScalar().withValue(value);
247 public void domSourceValue(final DOMSource value) {
248 // FIXME: tighten to concrete NormalizedNode.Builder interfaces
249 currentScalar().withValue(value);
253 @SuppressWarnings("rawtypes")
254 public void endNode() {
255 final var finishedBuilder = builders.poll();
256 checkState(finishedBuilder != null, "Node which should be closed does not exists.");
257 final var product = finishedBuilder.build();
264 public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
265 checkDataNodeContainer();
266 enter(name, BUILDER_FACTORY.newAnydataBuilder(objectModel));
267 // We support all object model
272 * Add a child not to the currently-open builder.
274 * @param child A new child
275 * @throws NullPointerException if {@code child} is null
276 * @throws IllegalStateException if there is no open builder
278 @SuppressWarnings({ "rawtypes", "unchecked" })
279 protected final void writeChild(final NormalizedNode child) {
280 final NormalizedNodeContainerBuilder current = currentContainer();
281 checkState(current != null, "Reached top level node, which could not be closed in this writer.");
282 current.addChild(requireNonNull(child));
285 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
286 @SuppressWarnings("rawtypes")
287 void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
288 builders.push(next.withNodeIdentifier(identifier));
292 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
293 protected final NormalizedNode.Builder popBuilder() {
294 return builders.pop();
297 final void reset(final NormalizationResultBuilder builder) {
300 builders.push(builder);
303 private <T> LeafNode.Builder<T> leafNodeBuilder(final DataSchemaNode schema) {
304 final var builder = this.<T>leafNodeBuilder();
305 if (schema instanceof LeafSchemaNode leafSchema) {
306 final var interner = LeafInterner.<LeafNode<T>>forSchema(leafSchema);
307 if (interner.isPresent()) {
308 return new InterningLeafNodeBuilder<>(builder, interner.orElseThrow());
314 <T> LeafNode.@NonNull Builder<T> leafNodeBuilder() {
315 return BUILDER_FACTORY.newLeafBuilder();
318 <T> LeafSetEntryNode.@NonNull Builder<T> leafsetEntryNodeBuilder() {
319 return BUILDER_FACTORY.newLeafSetEntryBuilder();
322 private void checkDataNodeContainer() {
323 @SuppressWarnings("rawtypes")
324 final NormalizedNodeContainerBuilder current = currentContainer();
325 if (!(current instanceof NormalizationResultBuilder)) {
326 checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
330 private NormalizedNode.Builder current() {
331 return builders.peek();
334 @SuppressWarnings("rawtypes")
335 private NormalizedNodeContainerBuilder currentContainer() {
336 final var current = current();
337 if (current instanceof NormalizedNodeContainerBuilder builder) {
340 if (current != null) {
341 throw new IllegalStateException(current + " is not a node container");
346 @SuppressWarnings("rawtypes")
347 private NormalizedNodeBuilder currentScalar() {
348 final var current = current();
349 if (current instanceof NormalizedNodeContainerBuilder) {
350 throw new IllegalStateException("Unexpected node container " + current);
352 if (current instanceof NormalizedNodeBuilder builder) {
355 throw new IllegalStateException("Unexpected non-scalar " + current);