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.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
27 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
28 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 * Event Stream Writer based on Normalized Node tree representation.
33 * <h2>Writing Event Stream</h2>
34 * Each entity is emitted by invoking its corresponding {@code start*} event, optionally followed by interior events and
35 * invoking {@link #endNode()}. Some entities supported nested entities, some do not, see below for restrictions.
38 * While this interface defines basic events, the event stream may be extended through {@link Extension}s, discoverable
39 * through {@link #supportedExtensions()} method. The set of these extensions is immutable during the lifetime of a
40 * writer and may be freely cached.
43 * <li>{@code container} - Container node representation, start event is emitted using
44 * {@link #startContainerNode(NodeIdentifier, int)}.
47 * <li>{@code list} - YANG list statement has two representation in event stream - unkeyed list and map. An unkeyed
48 * list is YANG list which did not specify a {@code key} statement. A map is a {@code list} with a {@code key}
51 * <li>{@code Map} - Map start event is emitted using {@link #startMapNode(NodeIdentifier, int)}. Each map entry start
52 * is emitted using {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
54 * <li>{@code UnkeyedList} - Unkeyed list represent list without keys, unkeyed list start is emitted using
55 * {@link #startUnkeyedList(NodeIdentifier, int)}. Each list item is emitted using
56 * {@link #startUnkeyedListItem(NodeIdentifier, int)}.</li>
60 * <li>{@code leaf} - Leaf node start event is emitted using {@link #startLeafNode(NodeIdentifier)}. Leaf node values
61 * need to be emitted through {@link #scalarValue(Object)}.
64 * <li>{@code leaf-list} - Leaf list start is emitted using {@link #startLeafSet(NodeIdentifier, int)}. Individual
65 * leaf-list entries are emitted using {@link #startLeafSetEntryNode(NodeWithValue)}.
67 * <li>{@code anyxml} - An anyxml node event is emitted using {@link #startAnyxmlNode(NodeIdentifier, Class)}.</li>
69 * <li>{@code choice} - Choice node event is emitted by {@link #startChoiceNode(NodeIdentifier, int)} event.</li>
72 * <h3>Implementation notes</h3>
75 * Implementations of this interface must not hold user suppled objects and resources needlessly.
77 public interface NormalizedNodeStreamWriter extends Closeable, Flushable,
78 ExtensibleObject<NormalizedNodeStreamWriter, NormalizedNodeStreamWriter.Extension> {
80 * Methods in this interface allow users to hint the underlying implementation about the sizing of container-like
81 * constructors (leafLists, containers, etc.). These hints may be taken into account by a particular implementation
82 * to improve performance, but clients are not required to provide hints. This constant should be used by clients
83 * who either do not have the sizing information, or do not wish to divulge it (for whatever reasons).
86 * Implementations are free to ignore these hints completely, but if they do use them, they are expected to be
87 * resilient in face of missing and mismatched hints, which is to say the user can specify startLeafSet(..., 1) and
88 * then call leafNode() 15 times.
91 * The acceptable hint values are non-negative integers and this constant, all other values will result, based on
92 * implementation preference, in the hint being completely ignored or IllegalArgumentException being thrown.
94 int UNKNOWN_SIZE = -1;
97 * Emits a start of leaf node event.
99 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
100 * @throws NullPointerException if {@code name} is null
101 * @throws IllegalArgumentException If emitted leaf node was emitted multiple times.
102 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
104 * @throws IOException if an underlying IO error occurs
106 void startLeafNode(NodeIdentifier name) throws IOException;
109 * Emits a start of system-ordered leaf set (leaf-list). While this entity is open,
110 * only {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations are free to reorder entries
111 * within the leaf-list.
113 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
114 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
115 * unknown. This is only hint and should not fail writing of child events, if there are more
117 * @throws NullPointerException if {@code name} is null
118 * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
120 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
122 * @throws IOException if an underlying IO error occurs
124 void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
127 * Emits a start of a user-ordered leaf set (leaf-list). While this entity is open, only
128 * {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations must retain the same entry order.
130 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
131 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
132 * unknown. This is only hint and should not fail writing of child events, if there are more
134 * @throws NullPointerException if {@code name} is null
135 * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
137 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
139 * @throws IOException if an underlying IO error occurs
141 void startOrderedLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
144 * Emits a leaf set entry node.
146 * @param name name of the node as defined in the schema.
147 * @throws NullPointerException if {@code name} is null
148 * @throws IllegalArgumentException if {@code name} does not match enclosing leaf set entity
149 * @throws IllegalStateException If node was emitted outside {@code leaf set} node.
150 * @throws IOException if an underlying IO error occurs
152 void startLeafSetEntryNode(NodeWithValue<?> name) throws IOException;
155 * Emits start of new container. Valid sub-events are:
157 * <li>{@link #startLeafNode}</li>
158 * <li>{@link #startAnyxmlNode(NodeIdentifier, Class)}</li>
159 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
160 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
161 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
162 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
163 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
166 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
167 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
168 * unknown. This is only hint and should not fail writing of child events, if there are more
170 * @throws NullPointerException if {@code name} is null
171 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
172 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
174 * @throws IOException if an underlying IO error occurs
176 void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException;
179 * Emits start of unkeyed list node event. Valid subevents is only
180 * {@link #startUnkeyedListItem(NodeIdentifier, int)}.
182 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
183 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
184 * unknown. This is only hint and should not fail writing of child events, if there are more
186 * @throws NullPointerException if {@code name} is null
187 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
188 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
190 * @throws IOException if an underlying IO error occurs
192 void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException;
195 * Emits start of new unkeyed list item. Valid sub-events are:
197 * <li>{@link #startLeafNode}</li>
198 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
199 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
200 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
201 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
202 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
205 * @param name Identifier of node
206 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
207 * unknown. This is only hint and should not fail writing of child events, if there are more
209 * @throws NullPointerException if {@code name} is null
210 * @throws IllegalStateException If node was emitted outside <code>unkeyed list</code> node.
211 * @throws IOException if an underlying IO error occurs
213 void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException;
216 * Emits start of map node event. Valid subevent is only
217 * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
219 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
220 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
221 * unknown. This is only hint and should not fail writing of child events, if there are more
223 * @throws NullPointerException if {@code name} is null
224 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
225 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
227 * @throws IOException if an underlying IO error occurs
229 void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
232 * Emits start of map entry. Valid sub-events are:
234 * <li>{@link #startLeafNode}</li>
235 * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
236 * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
237 * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
238 * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
239 * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
242 * @param identifier QName to value pairs of keys of map entry node.
243 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
244 * unknown. This is only hint and should not fail writing of child events, if there are more
246 * @throws NullPointerException if {@code name} is null
247 * @throws IllegalArgumentException If key contains incorrect value.
248 * @throws IllegalStateException If node was emitted outside {@code map entry} node.
249 * @throws IOException if an underlying IO error occurs
251 void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException;
254 * Emits start of map node event. Valid subevent is only
255 * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
257 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
258 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
259 * unknown. This is only hint and should not fail writing of child events, if there are more
261 * @throws NullPointerException if {@code name} is null
262 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
263 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
265 * @throws IOException if an underlying IO error occurs
267 void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
270 * Emits start of a choice node event.
272 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
273 * @param childSizeHint Non-negative count of expected direct child nodes or {@link #UNKNOWN_SIZE} if count is
274 * unknown. This is only hint and should not fail writing of child events, if there are more
276 * @throws NullPointerException if {@code name} is null
277 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
278 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
280 * @throws IOException if an underlying IO error occurs
282 void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException;
285 * Start emitting a new anydata node identified by name.
287 * @param name The name of the anydata element
288 * @param objectModel The object model of anydata content
289 * @return True if the specified object model is supported by this extension and the process of emitting the node
290 * has started. False if the object model is not supported and the node has not started to be emitted.
291 * @throws NullPointerException if any argument is null
292 * @throws IOException if an underlying IO error occurs
295 boolean startAnydataNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
298 * Emits a start of anyxml node event.
300 * @param name name of node as defined in schema, namespace and revision are derived from parent node.
301 * @param objectModel The object model of anyxml content
302 * @return True if the specified object model is supported by this extension and the process of emitting the node
303 * has started. False if the object model is not supported and the node has not started to be emitted.
304 * @throws NullPointerException if any argument is null
305 * @throws IllegalArgumentException If emitted node is invalid in current context or was emitted multiple times.
306 * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
308 * @throws IOException if an underlying IO error occurs
310 boolean startAnyxmlNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
313 * Set the value of current anyxml node. This call is only valid within the context in which an anyxml node is open.
315 * @param value node value
316 * @throws NullPointerException if the argument is null
317 * @throws IllegalArgumentException if the argument does not represents a valid value
318 * @throws IllegalStateException if an anyxml node is not open or if it's value has already been set and this
319 * implementation does not allow resetting the value.
320 * @throws IOException if an underlying IO error occurs
322 // FIXME: 7.0.0: this probably should integrated with scalarValue()
323 void domSourceValue(DOMSource value) throws IOException;
326 * Emits end event for node.
328 * @throws IllegalStateException If there is no start* event to be closed.
329 * @throws IOException if an underlying IO error occurs
331 void endNode() throws IOException;
334 * Attach the specified {@link DataSchemaNode} to the next node which will get started or emitted. The default
335 * implementation does nothing.
337 * @param schema DataSchemaNode
338 * @throws NullPointerException if the argument is null
340 default void nextDataSchemaNode(final @NonNull DataSchemaNode schema) {
341 requireNonNull(schema);
345 * Set the value of current node. This call is only valid within the context in which a value-bearing node is open,
346 * such as a LeafNode, LeafSetEntryNode.
348 * @param value node value, must be effectively immutable
349 * @throws NullPointerException if the argument is null
350 * @throws IllegalArgumentException if the argument does not represents a valid value
351 * @throws IllegalStateException if a value-bearing node is not open or if it's value has already been set and this
352 * implementation does not allow resetting the value.
353 * @throws IOException if an underlying IO error occurs
355 void scalarValue(@NonNull Object value) throws IOException;
358 void close() throws IOException;
361 void flush() throws IOException;
364 * Extension interface for {@link NormalizedNodeStreamWriter}. Extensions should extend this interface and their
365 * instances should be made available through {@link NormalizedNodeStreamWriter#supportedExtensions()}.
367 interface Extension extends ObjectExtension<NormalizedNodeStreamWriter, Extension> {
372 * Extension to the NormalizedNodeStreamWriter with metadata support. Semantically this extends the event model of
373 * {@link NormalizedNodeStreamWriter} with a new event, {@link #metadata(ImmutableMap)}. This event is valid on any
374 * open node. This event may be emitted only once.
377 * Note that some implementations of this interface, notably those targeting streaming XML, may require metadata to
378 * be emitted before any other events. Such requirement is communicated through {@link #requireMetadataFirst()} and
379 * users must honor it. If such requirement is not set, metadata may be emitted at any time.
382 * Furthermore implementations targeting RFC7952 encoding towards external systems are required to handle metadata
383 * attached to {@code leaf-list} and {@code list} nodes by correctly extending them to each entry.
385 interface MetadataExtension extends Extension {
387 * Emit a block of metadata associated with the currently-open node. The argument is a map of annotation names,
388 * as defined {@code md:annotation} extension. Values are normalized objects, which are required to be
389 * effectively-immutable.
391 * @param metadata Metadata block
392 * @throws NullPointerException if {@code metadata} is {@code null}
393 * @throws IllegalStateException when this method is invoked outside of an open node or metadata has already
395 * @throws IOException if an underlying IO error occurs
397 void metadata(ImmutableMap<QName, Object> metadata) throws IOException;
400 * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
403 * @return {@code true} if metadata must occur just after the start of an entry.
405 default boolean requireMetadataFirst() {
411 * An {@link Extension} exposed by stream writers which can handle mount point data, notably providing
412 * the facilities to resolve a mount point schema and normalize mount point contents into a normalized structure.
415 interface MountPointExtension extends Extension {
417 * Start a new mount point with a specific mount point context. The returned writer will be used to emit the
418 * content of the mount point, without touching the writer to which this extension is attached to. Once that is
419 * done, the returned writer will be {@link NormalizedNodeStreamWriter#close()}d, at which point the parent
420 * writer will be used again to emit the rest of the tree.
422 * @param label Mount point label
423 * @param mountCtx Mount point context
424 * @return A new NormalizedNodeStreamWriter
425 * @throws IOException if an error occurs
427 NormalizedNodeStreamWriter startMountPoint(MountPointLabel label, MountPointContext mountCtx)