eb31e4a4fd832bac3311615c85a01ca7d53dfbd0
[yangtools.git] / data / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / schema / stream / NormalizedNodeStreamWriter.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.api.schema.stream;
9
10 import static java.util.Objects.requireNonNull;
11
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;
30
31 /**
32  * Event Stream Writer based on Normalized Node tree representation.
33  *
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.
37  *
38  * <p>
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.
42  *
43  * <ul>
44  * <li>{@code container} - Container node representation, start event is emitted using
45  * {@link #startContainerNode(NodeIdentifier, int)}.
46  * </li>
47  *
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}
50  * statement.
51  * <ul>
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)}.
54  * </li>
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>
58  * </ul>
59  * </li>
60  *
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)}.
63  * </li>
64  *
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)}.
67  *
68  * <li>{@code anyxml} - An anyxml node event is emitted using {@link #startAnyxmlNode(NodeIdentifier, Class)}.</li>
69  *
70  * <li>{@code choice} - Choice node event is emitted by {@link #startChoiceNode(NodeIdentifier, int)} event.</li>
71  *
72  * <li>{@code augment} - Represents augmentation, augmentation node is started by invoking
73  * {@link #startAugmentationNode(AugmentationIdentifier)}.
74  * </li>
75  * </ul>
76  *
77  * <h3>Implementation notes</h3>
78  *
79  * <p>
80  * Implementations of this interface must not hold user suppled objects and resources needlessly.
81  */
82 public interface NormalizedNodeStreamWriter extends Closeable, Flushable,
83         ExtensibleObject<NormalizedNodeStreamWriter, NormalizedNodeStreamWriter.Extension> {
84     /**
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).
89      *
90      * <p>
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.
94      *
95      * <p>
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.
98      */
99     int UNKNOWN_SIZE = -1;
100
101     /**
102      * Emits a start of leaf node event.
103      *
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}
108      *                               node.
109      * @throws IOException if an underlying IO error occurs
110      */
111     void startLeafNode(NodeIdentifier name) throws IOException;
112
113     /**
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.
117      *
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
121      *                      events than count.
122      * @throws NullPointerException if {@code name} is null
123      * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
124      *                                  times.
125      * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
126      *                               node.
127      * @throws IOException if an underlying IO error occurs
128      */
129     void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
130
131     /**
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.
134      *
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
138      *                      events than count.
139      * @throws NullPointerException if {@code name} is null
140      * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
141      *                                  times.
142      * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
143      *                               node.
144      * @throws IOException if an underlying IO error occurs
145      */
146     void startOrderedLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
147
148     /**
149      * Emits a leaf set entry node.
150      *
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
156      */
157     void startLeafSetEntryNode(NodeWithValue<?> name) throws IOException;
158
159     /**
160      * Emits start of new container. Valid sub-events are:
161      * <ul>
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>
170      * </ul>
171      *
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
175      *                      events than count.
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}
179      *                               node.
180      * @throws IOException if an underlying IO error occurs
181      */
182     void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException;
183
184     /**
185      * Emits start of unkeyed list node event. Valid subevents is only
186      * {@link #startUnkeyedListItem(NodeIdentifier, int)}.
187      *
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
191      *                      events than count.
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}
195      *                               node.
196      * @throws IOException if an underlying IO error occurs
197      */
198     void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException;
199
200     /**
201      * Emits start of new unkeyed list item. Valid sub-events are:
202      * <ul>
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>
210      * </ul>
211      *
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
215      *                      events than count.
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
219      */
220     void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException;
221
222     /**
223      * Emits start of map node event. Valid subevent is only
224      * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
225      *
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
229      *                      events than count.
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}
233      *                               node.
234      * @throws IOException if an underlying IO error occurs
235      */
236     void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
237
238     /**
239      * Emits start of map entry. Valid sub-events are:
240      * <ul>
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>
248      * </ul>
249      *
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
253      *                      events than count.
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
258      */
259     void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException;
260
261     /**
262      * Emits start of map node event.  Valid subevent is only
263      * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
264      *
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
268      *                      events than count.
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}
272      *                               node.
273      * @throws IOException if an underlying IO error occurs
274      */
275     void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
276
277     /**
278      * Emits start of a choice node event.
279      *
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
283      *                      events than count.
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}
287      *                               node.
288      * @throws IOException if an underlying IO error occurs
289      */
290     void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException;
291
292     /**
293      * Emits start of augmentation node. Valid sub-events are:
294      * <ul>
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>
301      * </ul>
302      *
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
307      */
308     void startAugmentationNode(AugmentationIdentifier identifier) throws IOException;
309
310     /**
311      * Start emitting a new anydata node identified by name.
312      *
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
319      */
320     @Beta
321     boolean startAnydataNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
322
323     /**
324      * Emits a start of anyxml node event.
325      *
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}
333      *                               node.
334      * @throws IOException if an underlying IO error occurs
335      */
336     boolean startAnyxmlNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
337
338     /**
339      * Set the value of current anyxml node. This call is only valid within the context in which an anyxml node is open.
340      *
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
347      */
348     // FIXME: 7.0.0: this probably should integrated with scalarValue()
349     void domSourceValue(DOMSource value) throws IOException;
350
351     /**
352      * Emits end event for node.
353      *
354      * @throws IllegalStateException If there is no start* event to be closed.
355      * @throws IOException if an underlying IO error occurs
356      */
357     void endNode() throws IOException;
358
359     /**
360      * Attach the specified {@link DataSchemaNode} to the next node which will get started or emitted. The default
361      * implementation does nothing.
362      *
363      * @param schema DataSchemaNode
364      * @throws NullPointerException if the argument is null
365      */
366     default void nextDataSchemaNode(final @NonNull DataSchemaNode schema) {
367         requireNonNull(schema);
368     }
369
370     /**
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.
373      *
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
380      */
381     void scalarValue(@NonNull Object value) throws IOException;
382
383     @Override
384     void close() throws IOException;
385
386     @Override
387     void flush() throws IOException;
388
389     /**
390      * Extension interface for {@link NormalizedNodeStreamWriter}. Extensions should extend this interface and their
391      * instances should be made available through {@link NormalizedNodeStreamWriter#supportedExtensions()}.
392      */
393     interface Extension extends ObjectExtension<NormalizedNodeStreamWriter, Extension> {
394         // Marker interface
395     }
396
397     /**
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.
401      *
402      * <p>
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.
406      *
407      * <p>
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.
410      */
411     interface MetadataExtension extends Extension {
412         /**
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.
416          *
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
420          *                               been emitted.
421          * @throws IOException if an underlying IO error occurs
422          */
423         void metadata(ImmutableMap<QName, Object> metadata) throws IOException;
424
425         /**
426          * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
427          * returns false.
428          *
429          * @return {@code true} if metadata must occur just after the start of an entry.
430          */
431         default boolean requireMetadataFirst() {
432             return false;
433         }
434     }
435
436     /**
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.
439      */
440     @NonNullByDefault
441     interface MountPointExtension extends Extension {
442         /**
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.
447          *
448          * @param label Mount point label
449          * @param mountCtx Mount point context
450          * @return A new NormalizedNodeStreamWriter
451          * @throws IOException if an error occurs
452          */
453         NormalizedNodeStreamWriter startMountPoint(MountPointLabel label, MountPointContext mountCtx)
454             throws IOException;
455     }
456 }