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.AugmentationIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
26 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
28 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
29 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnydataNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserLeafSetNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserMapNodeBuilder;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
46 * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
47 * {@link NormalizedNode}s.
50 * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)} where resulting
51 * NormalizedNode will be stored in supplied result object.
54 * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
55 * written to this builder.
58 * This class is not final for purposes of customization, normal users should not need to subclass it.
60 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
61 @SuppressWarnings("rawtypes")
62 private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
64 private DataSchemaNode nextSchema;
66 @SuppressWarnings("rawtypes")
67 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
68 builders.push(topLevelBuilder);
71 protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeResult result) {
72 this(new NormalizedNodeResultBuilder(result));
76 * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied {@link NormalizedNode}s
77 * and writes them to supplied builder as child nodes.
80 * Type of supplied {@link NormalizedNodeContainerBuilder} affects, which events could be emitted in order
81 * to ensure proper construction of data.
83 * @param builder Builder to which data will be written.
84 * @return {@link NormalizedNodeStreamWriter} which writes data
86 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
87 return new ImmutableNormalizedNodeStreamWriter(builder);
91 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
92 * (type of NormalizedNode) is determined by first start event.
95 * Result is built when {@link #endNode()} associated with that start event is emitted.
98 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
102 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
103 * type of {@link NormalizedNode} will be written.
105 * @param result {@link NormalizedNodeResult} object which will hold result value.
106 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
108 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
109 return result instanceof NormalizedNodeMetadataResult ? from((NormalizedNodeMetadataResult) result)
110 : new ImmutableNormalizedNodeStreamWriter(result);
114 * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top-level {@link NormalizedNode}
115 * (type of NormalizedNode) is determined by first start event.
118 * Result is built when {@link #endNode()} associated with that start event is emitted.
121 * Writer properly creates also nested {@link NormalizedNode} instances, if their are supported inside the scope
122 * of the first event.
125 * This method is useful for clients, which knows there will be one top-level node written, but does not know which
126 * type of {@link NormalizedNode} will be written.
128 * @param result {@link NormalizedNodeResult} object which will hold result value.
129 * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
131 public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeMetadataResult result) {
132 return new ImmutableMetadataNormalizedNodeStreamWriter(result);
136 public void startLeafNode(final NodeIdentifier name) {
137 checkDataNodeContainer();
138 enter(name, leafNodeBuilder(nextSchema));
143 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
144 checkDataNodeContainer();
145 enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
146 : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
150 public void startLeafSetEntryNode(final NodeWithValue<?> name) {
151 final NormalizedNodeBuilder<?, ?, ?> current = current();
152 checkArgument(current instanceof ImmutableLeafSetNodeBuilder
153 || current instanceof ImmutableUserLeafSetNodeBuilder || current instanceof NormalizedNodeResultBuilder,
154 "LeafSetEntryNode is not valid for parent %s", current);
155 enter(name, leafsetEntryNodeBuilder());
160 public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
161 checkDataNodeContainer();
162 enter(name, Builders.orderedLeafSetBuilder());
166 public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
167 checkDataNodeContainer();
168 if (DOMSource.class.isAssignableFrom(objectModel)) {
169 enter(name, ImmutableAnyXmlNodeBuilder.create());
177 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
178 checkDataNodeContainer();
179 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
180 : ImmutableContainerNodeBuilder.create(childSizeHint));
184 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
185 checkDataNodeContainer();
186 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
187 : ImmutableUnkeyedListNodeBuilder.create(childSizeHint));
191 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
192 final NormalizedNodeBuilder<?, ?, ?> current = current();
193 checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
194 || current instanceof NormalizedNodeResultBuilder);
195 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
196 : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint));
200 public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
201 checkDataNodeContainer();
202 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableMapNodeBuilder.create()
203 : ImmutableMapNodeBuilder.create(childSizeHint));
207 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
208 final NormalizedNodeBuilder<?, ?, ?> current = current();
209 checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableUserMapNodeBuilder
210 || current instanceof NormalizedNodeResultBuilder);
212 enter(identifier, UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
213 : ImmutableMapEntryNodeBuilder.create(childSizeHint));
217 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
218 checkDataNodeContainer();
219 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUserMapNodeBuilder.create()
220 : ImmutableUserMapNodeBuilder.create(childSizeHint));
224 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
225 checkDataNodeContainer();
226 enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableChoiceNodeBuilder.create()
227 : ImmutableChoiceNodeBuilder.create(childSizeHint));
231 public void startAugmentationNode(final AugmentationIdentifier identifier) {
232 checkDataNodeContainer();
233 checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
234 enter(identifier, Builders.augmentationBuilder());
238 public void flush() {
243 public void close() {
248 public void nextDataSchemaNode(final DataSchemaNode schema) {
249 nextSchema = requireNonNull(schema);
253 public void scalarValue(final Object value) {
254 currentScalar().withValue(value);
258 public void domSourceValue(final DOMSource value) {
259 currentScalar().withValue(value);
263 @SuppressWarnings({ "rawtypes", "unchecked" })
264 public void endNode() {
265 final NormalizedNodeBuilder finishedBuilder = builders.poll();
266 checkState(finishedBuilder != null, "Node which should be closed does not exists.");
267 final NormalizedNode product = finishedBuilder.build();
274 public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
275 checkDataNodeContainer();
276 enter(name, ImmutableAnydataNodeBuilder.create(objectModel));
277 // We support all object models
282 * Add a child not to the currently-open builder.
284 * @param child A new child
285 * @throws NullPointerException if {@code child} is null
286 * @throws IllegalStateException if there is no open builder
288 @SuppressWarnings({ "rawtypes", "unchecked" })
289 protected final void writeChild(final NormalizedNode child) {
290 final NormalizedNodeContainerBuilder current = currentContainer();
291 checkState(current != null, "Reached top level node, which could not be closed in this writer.");
292 current.addChild(requireNonNull(child));
295 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
296 @SuppressWarnings("rawtypes")
297 void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
298 builders.push(next.withNodeIdentifier(identifier));
302 // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
303 protected final NormalizedNodeBuilder popBuilder() {
304 return builders.pop();
307 final void reset(final NormalizedNodeResultBuilder builder) {
310 builders.push(builder);
313 private <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder(final DataSchemaNode schema) {
314 final InterningLeafNodeBuilder<T> interning = InterningLeafNodeBuilder.forSchema(schema);
315 return interning != null ? interning : leafNodeBuilder();
318 <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
319 return new ImmutableLeafNodeBuilder<>();
322 <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
323 return ImmutableLeafSetEntryNodeBuilder.create();
326 private void checkDataNodeContainer() {
327 @SuppressWarnings("rawtypes")
328 final NormalizedNodeContainerBuilder current = currentContainer();
329 if (!(current instanceof NormalizedNodeResultBuilder)) {
330 checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
334 @SuppressWarnings("rawtypes")
335 private NormalizedNodeBuilder current() {
336 return builders.peek();
339 @SuppressWarnings("rawtypes")
340 private NormalizedNodeContainerBuilder currentContainer() {
341 final NormalizedNodeBuilder current = current();
342 if (current == null) {
345 checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
346 return (NormalizedNodeContainerBuilder) current;
349 @SuppressWarnings("rawtypes")
350 private NormalizedNodeBuilder currentScalar() {
351 final NormalizedNodeBuilder current = current();
352 checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);