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.api.schema.stream;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableMap;
14 import java.io.Closeable;
15 import java.io.Flushable;
16 import java.io.IOException;
17 import javax.xml.transform.dom.DOMSource;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.opendaylight.yangtools.concepts.ExtensibleObject;
21 import org.opendaylight.yangtools.concepts.ObjectExtension;
22 import org.opendaylight.yangtools.rfc8528.model.api.MountPointLabel;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 * Event Stream Writer based on Normalized Node tree representation.
34 * <h2>Writing Event Stream</h2>
35 * Each entity is emitted by invoking its corresponding {@code start*} event, optionally followed by interior events and
36 * invoking {@link #endNode()}. Some entities supported nested entities, some do not, see below for restrictions.
39 * While this interface defines basic events, the event stream may be extended through {@link Extension}s, discoverable
40 * through {@link #supportedExtensions()} method. The set of these extensions is immutable during the lifetime of a
41 * writer and may be freely cached.
44 * <li>{@code container} - Container node representation, start event is emitted using
45 * {@link #startContainerNode(NodeIdentifier, int)}.
48 * <li>{@code list} - YANG list statement has two representation in event stream - unkeyed list and map. An unkeyed
49 * list is YANG list which did not specify a {@code key} statement. A map is a {@code list} with a {@code key}
52 * <li>{@code Map} - Map start event is emitted using {@link #startMapNode(NodeIdentifier, int)}. Each map entry start
53 * is emitted using {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
55 * <li>{@code UnkeyedList} - Unkeyed list represent list without keys, unkeyed list start is emitted using
56 * {@link #startUnkeyedList(NodeIdentifier, int)}. Each list item is emitted using
57 * {@link #startUnkeyedListItem(NodeIdentifier, int)}.</li>
61 * <li>{@code leaf} - Leaf node start event is emitted using {@link #startLeafNode(NodeIdentifier)}. Leaf node values
62 * need to be emitted through {@link #scalarValue(Object)}.
65 * <li>{@code leaf-list} - Leaf list start is emitted using {@link #startLeafSet(NodeIdentifier, int)}. Individual
66 * leaf-list entries are emitted using {@link #startLeafSetEntryNode(NodeWithValue)}.
68 * <li>{@code anyxml} - An anyxml node event is emitted using {@link #startAnyxmlNode(NodeIdentifier, Class)}.</li>
70 * <li>{@code choice} - Choice node event is emitted by {@link #startChoiceNode(NodeIdentifier, int)} event.</li>
72 * <li>{@code augment} - Represents augmentation, augmentation node is started by invoking
73 * {@link #startAugmentationNode(AugmentationIdentifier)}.
77 * <h3>Implementation notes</h3>
80 * Implementations of this interface must not hold user suppled objects and resources needlessly.
82 public interface NormalizedNodeStreamWriter extends Closeable, Flushable,
83 ExtensibleObject<NormalizedNodeStreamWriter, NormalizedNodeStreamWriter.Extension> {
85 * Methods in this interface allow users to hint the underlying implementation about the sizing of container-like
86 * constructors (leafLists, containers, etc.). These hints may be taken into account by a particular implementation
87 * to improve performance, but clients are not required to provide hints. This constant should be used by clients
88 * who either do not have the sizing information, or do not wish to divulge it (for whatever reasons).
91 * Implementations are free to ignore these hints completely, but if they do use them, they are expected to be
92 * resilient in face of missing and mismatched hints, which is to say the user can specify startLeafSet(..., 1) and
93 * then call leafNode() 15 times.
96 * The acceptable hint values are non-negative integers and this constant, all other values will result, based on
97 * implementation preference, in the hint being completely ignored or IllegalArgumentException being thrown.
99 int UNKNOWN_SIZE = -1;
102 * Emits a start of leaf node event.
104 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
105 * @throws NullPointerException if {@code name} is null
106 * @throws IllegalArgumentException If emitted leaf node was emitted multiple times.
107 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
109 * @throws IOException if an underlying IO error occurs
111 void startLeafNode(NodeIdentifier name) throws IOException;
114 * Emits a start of system-ordered leaf set (leaf-list). While this entity is open,
115 * only {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations are free to reorder entries
116 * within the leaf-list.
118 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
119 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
120 * unknown. This is only hint and should not fail writing of child events, if there are more
122 * @throws NullPointerException if {@code name} is null
123 * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
125 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
127 * @throws IOException if an underlying IO error occurs
129 void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
132 * Emits a start of a user-ordered leaf set (leaf-list). While this entity is open, only
133 * {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations must retain the same entry order.
135 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
136 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
137 * unknown. This is only hint and should not fail writing of child events, if there are more
139 * @throws NullPointerException if {@code name} is null
140 * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
142 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
144 * @throws IOException if an underlying IO error occurs
146 void startOrderedLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
149 * Emits a leaf set entry node.
151 * @param name name of the node as defined in the schema.
152 * @throws NullPointerException if {@code name} is null
153 * @throws IllegalArgumentException if {@code name} does not match enclosing leaf set entity
154 * @throws IllegalStateException If node was emitted outside {@code leaf set} node.
155 * @throws IOException if an underlying IO error occurs
157 void startLeafSetEntryNode(NodeWithValue<?> name) throws IOException;
160 * Emits start of new container. Valid sub-events are:
162 * <li>{@link #startLeafNode}</li>
163 * <li>{@link #startAnyxmlNode(NodeIdentifier, Class)}</li>
164 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
165 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
166 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
167 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
168 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
169 * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
172 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
173 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
174 * unknown. This is only hint and should not fail writing of child events, if there are more
176 * @throws NullPointerException if {@code name} is null
177 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
178 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
180 * @throws IOException if an underlying IO error occurs
182 void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException;
185 * Emits start of unkeyed list node event. Valid subevents is only
186 * {@link #startUnkeyedListItem(NodeIdentifier, int)}.
188 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
189 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
190 * unknown. This is only hint and should not fail writing of child events, if there are more
192 * @throws NullPointerException if {@code name} is null
193 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
194 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
196 * @throws IOException if an underlying IO error occurs
198 void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException;
201 * Emits start of new unkeyed list item. Valid sub-events are:
203 * <li>{@link #startLeafNode}</li>
204 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
205 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
206 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
207 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
208 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
209 * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
212 * @param name Identifier of node
213 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
214 * unknown. This is only hint and should not fail writing of child events, if there are more
216 * @throws NullPointerException if {@code name} is null
217 * @throws IllegalStateException If node was emitted outside <code>unkeyed list</code> node.
218 * @throws IOException if an underlying IO error occurs
220 void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException;
223 * Emits start of map node event. Valid subevent is only
224 * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
226 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
227 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
228 * unknown. This is only hint and should not fail writing of child events, if there are more
230 * @throws NullPointerException if {@code name} is null
231 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
232 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
234 * @throws IOException if an underlying IO error occurs
236 void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
239 * Emits start of map entry. Valid sub-events are:
241 * <li>{@link #startLeafNode}</li>
242 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
243 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
244 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
245 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
246 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
247 * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
250 * @param identifier QName to value pairs of keys of map entry node.
251 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
252 * unknown. This is only hint and should not fail writing of child events, if there are more
254 * @throws NullPointerException if {@code name} is null
255 * @throws IllegalArgumentException If key contains incorrect value.
256 * @throws IllegalStateException If node was emitted outside {@code map entry} node.
257 * @throws IOException if an underlying IO error occurs
259 void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException;
262 * Emits start of map node event. Valid subevent is only
263 * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
265 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
266 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
267 * unknown. This is only hint and should not fail writing of child events, if there are more
269 * @throws NullPointerException if {@code name} is null
270 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
271 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
273 * @throws IOException if an underlying IO error occurs
275 void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
278 * Emits start of a choice node event.
280 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
281 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
282 * unknown. This is only hint and should not fail writing of child events, if there are more
284 * @throws NullPointerException if {@code name} is null
285 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
286 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
288 * @throws IOException if an underlying IO error occurs
290 void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException;
293 * Emits start of augmentation node. Valid sub-events are:
295 * <li>{@link #startLeafNode}</li>
296 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
297 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
298 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
299 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
300 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
303 * @param identifier Augmentation identifier
304 * @throws NullPointerException if {@code identifier} is null
305 * @throws IllegalArgumentException If augmentation is invalid in current context.
306 * @throws IOException if an underlying IO error occurs
308 void startAugmentationNode(AugmentationIdentifier identifier) throws IOException;
311 * Start emitting a new anydata node identified by name.
313 * @param name The name of the anydata element
314 * @param objectModel The object model of anydata content
315 * @return True if the specified object model is supported by this extension and the process of emitting the node
316 * has started. False if the object model is not supported and the node has not started to be emitted.
317 * @throws NullPointerException if any argument is null
318 * @throws IOException if an underlying IO error occurs
321 boolean startAnydataNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
324 * Emits a start of anyxml node event.
326 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
327 * @param objectModel The object model of anyxml content
328 * @return True if the specified object model is supported by this extension and the process of emitting the node
329 * has started. False if the object model is not supported and the node has not started to be emitted.
330 * @throws NullPointerException if any argument is null
331 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
332 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
334 * @throws IOException if an underlying IO error occurs
336 boolean startAnyxmlNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
339 * Set the value of current anyxml node. This call is only valid within the context in which an anyxml node is open.
341 * @param value node value
342 * @throws NullPointerException if the argument is null
343 * @throws IllegalArgumentException if the argument does not represents a valid value
344 * @throws IllegalStateException if an anyxml node is not open or if it's value has already been set and this
345 * implementation does not allow resetting the value.
346 * @throws IOException if an underlying IO error occurs
348 // FIXME: 7.0.0: this probably should integrated with scalarValue()
349 void domSourceValue(DOMSource value) throws IOException;
352 * Emits end event for node.
354 * @throws IllegalStateException If there is no start* event to be closed.
355 * @throws IOException if an underlying IO error occurs
357 void endNode() throws IOException;
360 * Attach the specified {@link DataSchemaNode} to the next node which will get started or emitted. The default
361 * implementation does nothing.
363 * @param schema DataSchemaNode
364 * @throws NullPointerException if the argument is null
366 default void nextDataSchemaNode(final @NonNull DataSchemaNode schema) {
367 requireNonNull(schema);
371 * Set the value of current node. This call is only valid within the context in which a value-bearing node is open,
372 * such as a LeafNode, LeafSetEntryNode.
374 * @param value node value, must be effectively immutable
375 * @throws NullPointerException if the argument is null
376 * @throws IllegalArgumentException if the argument does not represents a valid value
377 * @throws IllegalStateException if a value-bearing node is not open or if it's value has already been set and this
378 * implementation does not allow resetting the value.
379 * @throws IOException if an underlying IO error occurs
381 void scalarValue(@NonNull Object value) throws IOException;
384 void close() throws IOException;
387 void flush() throws IOException;
390 * Extension interface for {@link NormalizedNodeStreamWriter}. Extensions should extend this interface and their
391 * instances should be made available through {@link NormalizedNodeStreamWriter#supportedExtensions()}.
393 interface Extension extends ObjectExtension<NormalizedNodeStreamWriter, Extension> {
398 * Extension to the NormalizedNodeStreamWriter with metadata support. Semantically this extends the event model of
399 * {@link NormalizedNodeStreamWriter} with a new event, {@link #metadata(ImmutableMap)}. This event is valid on any
400 * open node. This event may be emitted only once.
403 * Note that some implementations of this interface, notably those targeting streaming XML, may require metadata to
404 * be emitted before any other events. Such requirement is communicated through {@link #requireMetadataFirst()} and
405 * users must honor it. If such requirement is not set, metadata may be emitted at any time.
408 * Furthermore implementations targeting RFC7952 encoding towards external systems are required to handle metadata
409 * attached to {@code leaf-list} and {@code list} nodes by correctly extending them to each entry.
411 interface MetadataExtension extends Extension {
413 * Emit a block of metadata associated with the currently-open node. The argument is a map of annotation names,
414 * as defined {@code md:annotation} extension. Values are normalized objects, which are required to be
415 * effectively-immutable.
417 * @param metadata Metadata block
418 * @throws NullPointerException if {@code metadata} is {@code null}
419 * @throws IllegalStateException when this method is invoked outside of an open node or metadata has already
421 * @throws IOException if an underlying IO error occurs
423 void metadata(ImmutableMap<QName, Object> metadata) throws IOException;
426 * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
429 * @return {@code true} if metadata must occur just after the start of an entry.
431 default boolean requireMetadataFirst() {
437 * An {@link Extension} exposed by stream writers which can handle mount point data, notably providing
438 * the facilities to resolve a mount point schema and normalize mount point contents into a normalized structure.
441 interface MountPointExtension extends Extension {
443 * Start a new mount point with a specific mount point context. The returned writer will be used to emit the
444 * content of the mount point, without touching the writer to which this extension is attached to. Once that is
445 * done, the returned writer will be {@link NormalizedNodeStreamWriter#close()}d, at which point the parent
446 * writer will be used again to emit the rest of the tree.
448 * @param label Mount point label
449 * @param mountCtx Mount point context
450 * @return A new NormalizedNodeStreamWriter
451 * @throws IOException if an error occurs
453 NormalizedNodeStreamWriter startMountPoint(MountPointLabel label, MountPointContext mountCtx)