Remove Augmentation{Identifier,Node}
[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.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;
29
30 /**
31  * Event Stream Writer based on Normalized Node tree representation.
32  *
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.
36  *
37  * <p>
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.
41  *
42  * <ul>
43  * <li>{@code container} - Container node representation, start event is emitted using
44  * {@link #startContainerNode(NodeIdentifier, int)}.
45  * </li>
46  *
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}
49  * statement.
50  * <ul>
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)}.
53  * </li>
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>
57  * </ul>
58  * </li>
59  *
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)}.
62  * </li>
63  *
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)}.
66  *
67  * <li>{@code anyxml} - An anyxml node event is emitted using {@link #startAnyxmlNode(NodeIdentifier, Class)}.</li>
68  *
69  * <li>{@code choice} - Choice node event is emitted by {@link #startChoiceNode(NodeIdentifier, int)} event.</li>
70  * </ul>
71  *
72  * <h3>Implementation notes</h3>
73  *
74  * <p>
75  * Implementations of this interface must not hold user suppled objects and resources needlessly.
76  */
77 public interface NormalizedNodeStreamWriter extends Closeable, Flushable,
78         ExtensibleObject<NormalizedNodeStreamWriter, NormalizedNodeStreamWriter.Extension> {
79     /**
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).
84      *
85      * <p>
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.
89      *
90      * <p>
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.
93      */
94     int UNKNOWN_SIZE = -1;
95
96     /**
97      * Emits a start of leaf node event.
98      *
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}
103      *                               node.
104      * @throws IOException if an underlying IO error occurs
105      */
106     void startLeafNode(NodeIdentifier name) throws IOException;
107
108     /**
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.
112      *
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
116      *                      events than count.
117      * @throws NullPointerException if {@code name} is null
118      * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
119      *                                  times.
120      * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
121      *                               node.
122      * @throws IOException if an underlying IO error occurs
123      */
124     void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
125
126     /**
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.
129      *
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
133      *                      events than count.
134      * @throws NullPointerException if {@code name} is null
135      * @throws IllegalArgumentException If emitted leaf node is invalid in current context or was emitted multiple
136      *                                  times.
137      * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
138      *                               node.
139      * @throws IOException if an underlying IO error occurs
140      */
141     void startOrderedLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
142
143     /**
144      * Emits a leaf set entry node.
145      *
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
151      */
152     void startLeafSetEntryNode(NodeWithValue<?> name) throws IOException;
153
154     /**
155      * Emits start of new container. Valid sub-events are:
156      * <ul>
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>
164      * </ul>
165      *
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
169      *                      events than count.
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}
173      *                               node.
174      * @throws IOException if an underlying IO error occurs
175      */
176     void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException;
177
178     /**
179      * Emits start of unkeyed list node event. Valid subevents is only
180      * {@link #startUnkeyedListItem(NodeIdentifier, int)}.
181      *
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
185      *                      events than count.
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}
189      *                               node.
190      * @throws IOException if an underlying IO error occurs
191      */
192     void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException;
193
194     /**
195      * Emits start of new unkeyed list item. Valid sub-events are:
196      * <ul>
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>
203      * </ul>
204      *
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
208      *                      events than count.
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
212      */
213     void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException;
214
215     /**
216      * Emits start of map node event. Valid subevent is only
217      * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
218      *
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
222      *                      events than count.
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}
226      *                               node.
227      * @throws IOException if an underlying IO error occurs
228      */
229     void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
230
231     /**
232      * Emits start of map entry. Valid sub-events are:
233      * <ul>
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>
240      * </ul>
241      *
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
245      *                      events than count.
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
250      */
251     void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException;
252
253     /**
254      * Emits start of map node event.  Valid subevent is only
255      * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
256      *
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
260      *                      events than count.
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}
264      *                               node.
265      * @throws IOException if an underlying IO error occurs
266      */
267     void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
268
269     /**
270      * Emits start of a choice node event.
271      *
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
275      *                      events than count.
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}
279      *                               node.
280      * @throws IOException if an underlying IO error occurs
281      */
282     void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException;
283
284     /**
285      * Start emitting a new anydata node identified by name.
286      *
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
293      */
294     @Beta
295     boolean startAnydataNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
296
297     /**
298      * Emits a start of anyxml node event.
299      *
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}
307      *                               node.
308      * @throws IOException if an underlying IO error occurs
309      */
310     boolean startAnyxmlNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
311
312     /**
313      * Set the value of current anyxml node. This call is only valid within the context in which an anyxml node is open.
314      *
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
321      */
322     // FIXME: 7.0.0: this probably should integrated with scalarValue()
323     void domSourceValue(DOMSource value) throws IOException;
324
325     /**
326      * Emits end event for node.
327      *
328      * @throws IllegalStateException If there is no start* event to be closed.
329      * @throws IOException if an underlying IO error occurs
330      */
331     void endNode() throws IOException;
332
333     /**
334      * Attach the specified {@link DataSchemaNode} to the next node which will get started or emitted. The default
335      * implementation does nothing.
336      *
337      * @param schema DataSchemaNode
338      * @throws NullPointerException if the argument is null
339      */
340     default void nextDataSchemaNode(final @NonNull DataSchemaNode schema) {
341         requireNonNull(schema);
342     }
343
344     /**
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.
347      *
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
354      */
355     void scalarValue(@NonNull Object value) throws IOException;
356
357     @Override
358     void close() throws IOException;
359
360     @Override
361     void flush() throws IOException;
362
363     /**
364      * Extension interface for {@link NormalizedNodeStreamWriter}. Extensions should extend this interface and their
365      * instances should be made available through {@link NormalizedNodeStreamWriter#supportedExtensions()}.
366      */
367     interface Extension extends ObjectExtension<NormalizedNodeStreamWriter, Extension> {
368         // Marker interface
369     }
370
371     /**
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.
375      *
376      * <p>
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.
380      *
381      * <p>
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.
384      */
385     interface MetadataExtension extends Extension {
386         /**
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.
390          *
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
394          *                               been emitted.
395          * @throws IOException if an underlying IO error occurs
396          */
397         void metadata(ImmutableMap<QName, Object> metadata) throws IOException;
398
399         /**
400          * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
401          * returns false.
402          *
403          * @return {@code true} if metadata must occur just after the start of an entry.
404          */
405         default boolean requireMetadataFirst() {
406             return false;
407         }
408     }
409
410     /**
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.
413      */
414     @NonNullByDefault
415     interface MountPointExtension extends Extension {
416         /**
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.
421          *
422          * @param label Mount point label
423          * @param mountCtx Mount point context
424          * @return A new NormalizedNodeStreamWriter
425          * @throws IOException if an error occurs
426          */
427         NormalizedNodeStreamWriter startMountPoint(MountPointLabel label, MountPointContext mountCtx)
428             throws IOException;
429     }
430 }