001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.marshall.stream;
018
019import java.io.*;
020import java.math.*;
021
022import org.apache.juneau.marshall.serializer.*;
023
024/**
025 * Public low-level push-generator that emits a Juneau-formatted document one structural event at a
026 * time.
027 *
028 * <p>
029 * The caller emits structural events ({@link #startObject()} / {@link #fieldName(String)} /
030 * {@link #number(long)} / ...) and the implementation produces the corresponding bytes for its
031 * format.  This is a format-neutral surface; the same writer methods work across every Juneau
032 * format that implements {@link TokenWritable} (the structural-token role).
033 *
034 * <h5 class='section'>Usage:</h5>
035 * <p class='bjava'>
036 *    StringBuilder <jv>sb</jv> = <jk>new</jk> StringBuilder();
037 *    <jk>try</jk> (TokenWriter <jv>w</jv> = Json.<jsf>DEFAULT</jsf>.writeTokens(<jv>sb</jv>)) {
038 *       <jv>w</jv>.startObject();
039 *       <jv>w</jv>.fieldName(<js>"a"</js>);
040 *       <jv>w</jv>.number(1);
041 *       <jv>w</jv>.fieldName(<js>"b"</js>);
042 *       <jv>w</jv>.startArray();
043 *       <jv>w</jv>.bool(<jk>true</jk>);
044 *       <jv>w</jv>.nil();
045 *       <jv>w</jv>.endArray();
046 *       <jv>w</jv>.endObject();
047 *    }
048 *    <jc>// sb is now: {"a":1,"b":[true,null]}</jc>
049 * </p>
050 *
051 * <h5 class='section'>Contract:</h5>
052 * <ul>
053 *    <li>The writer is <b>purely structural</b>.  It bypasses object swaps and {@code @Schema}
054 *       annotations &mdash; that is the role of the POJO databind path on
055 *       {@link Serializer Serializer} and its bridges.
056 *    <li>It is the caller's responsibility to emit a well-formed event sequence (every
057 *       {@code startObject()} matched by an {@code endObject()}, every field name followed by
058 *       exactly one value or container, etc.).  Implementations are not required to validate the
059 *       sequence, though they may.
060 *    <li>Concrete implementations are not required to be thread-safe.
061 * </ul>
062 *
063 * <h5 class='section'>See Also:</h5><ul>
064 *    <li>{@link TokenReader} &mdash; the pull-side counterpart.
065 *    <li>{@link TokenType} &mdash; the format-neutral token vocabulary.
066 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
067 * </ul>
068 */
069@SuppressWarnings({
070   "resource" // TokenWriter wraps the caller's writer; the underlying Closeable is caller-owned, so the resource-leak warning is by design.
071})
072public interface TokenWriter extends RecordWriter {
073
074   /**
075    * Emits a {@link TokenType#START_OBJECT} event.
076    *
077    * @return This object.
078    * @throws IOException If a problem occurred writing to the underlying stream.
079    * @throws SerializeException If the format cannot represent this event in the current state.
080    */
081   TokenWriter startObject() throws IOException;
082
083   /**
084    * Emits a {@link TokenType#END_OBJECT} event.
085    *
086    * @return This object.
087    * @throws IOException If a problem occurred writing to the underlying stream.
088    * @throws SerializeException If the format cannot represent this event in the current state.
089    */
090   TokenWriter endObject() throws IOException;
091
092   /**
093    * Emits a {@link TokenType#START_ARRAY} event.
094    *
095    * @return This object.
096    * @throws IOException If a problem occurred writing to the underlying stream.
097    * @throws SerializeException If the format cannot represent this event in the current state.
098    */
099   TokenWriter startArray() throws IOException;
100
101   /**
102    * Emits a {@link TokenType#END_ARRAY} event.
103    *
104    * @return This object.
105    * @throws IOException If a problem occurred writing to the underlying stream.
106    * @throws SerializeException If the format cannot represent this event in the current state.
107    */
108   TokenWriter endArray() throws IOException;
109
110   /**
111    * Emits a {@link TokenType#FIELD_NAME} event for the next field of the enclosing object.
112    *
113    * @param name The field name.  Must not be <jk>null</jk>.
114    * @return This object.
115    * @throws IOException If a problem occurred writing to the underlying stream.
116    * @throws SerializeException If the format cannot represent this event in the current state.
117    */
118   TokenWriter fieldName(String name) throws IOException;
119
120   /**
121    * Emits a {@link TokenType#VALUE_STRING} event.
122    *
123    * @param value The value.  May be <jk>null</jk>, in which case a {@link TokenType#VALUE_NULL}
124    *    event is emitted instead.
125    * @return This object.
126    * @throws IOException If a problem occurred writing to the underlying stream.
127    * @throws SerializeException If the format cannot represent this event in the current state.
128    */
129   TokenWriter string(String value) throws IOException;
130
131   /**
132    * Emits a {@link TokenType#VALUE_NUMBER} event for an arbitrary {@link Number}.
133    *
134    * @param value The value.  May be <jk>null</jk>, in which case a {@link TokenType#VALUE_NULL}
135    *    event is emitted instead.
136    * @return This object.
137    * @throws IOException If a problem occurred writing to the underlying stream.
138    * @throws SerializeException If the format cannot represent this event in the current state.
139    */
140   TokenWriter number(Number value) throws IOException;
141
142   /**
143    * Emits a {@link TokenType#VALUE_NUMBER} event for a {@code long}.
144    *
145    * @param value The value.
146    * @return This object.
147    * @throws IOException If a problem occurred writing to the underlying stream.
148    * @throws SerializeException If the format cannot represent this event in the current state.
149    */
150   TokenWriter number(long value) throws IOException;
151
152   /**
153    * Emits a {@link TokenType#VALUE_NUMBER} event for a {@code double}.
154    *
155    * @param value The value.
156    * @return This object.
157    * @throws IOException If a problem occurred writing to the underlying stream.
158    * @throws SerializeException If the format cannot represent this event in the current state.
159    */
160   TokenWriter number(double value) throws IOException;
161
162   /**
163    * Emits a {@link TokenType#VALUE_NUMBER} event for a {@link BigDecimal}.
164    *
165    * @param value The value.  May be <jk>null</jk>, in which case a {@link TokenType#VALUE_NULL}
166    *    event is emitted instead.
167    * @return This object.
168    * @throws IOException If a problem occurred writing to the underlying stream.
169    * @throws SerializeException If the format cannot represent this event in the current state.
170    */
171   TokenWriter number(BigDecimal value) throws IOException;
172
173   /**
174    * Emits a {@link TokenType#VALUE_NUMBER} event for a {@link BigInteger}.
175    *
176    * @param value The value.  May be <jk>null</jk>, in which case a {@link TokenType#VALUE_NULL}
177    *    event is emitted instead.
178    * @return This object.
179    * @throws IOException If a problem occurred writing to the underlying stream.
180    * @throws SerializeException If the format cannot represent this event in the current state.
181    */
182   TokenWriter number(BigInteger value) throws IOException;
183
184   /**
185    * Emits a {@link TokenType#VALUE_BOOLEAN} event.
186    *
187    * @param value The value.
188    * @return This object.
189    * @throws IOException If a problem occurred writing to the underlying stream.
190    * @throws SerializeException If the format cannot represent this event in the current state.
191    */
192   TokenWriter bool(boolean value) throws IOException;
193
194   /**
195    * Emits a {@link TokenType#VALUE_NULL} event.
196    *
197    * @return This object.
198    * @throws IOException If a problem occurred writing to the underlying stream.
199    * @throws SerializeException If the format cannot represent this event in the current state.
200    */
201   TokenWriter nil() throws IOException;
202
203   /**
204    * Emits an arbitrary POJO through the format's structural events.
205    *
206    * <p>
207    * The value is walked via {@link PojoWalker}: nulls / scalars / collections / arrays / maps /
208    * beans are introspected and re-emitted through this writer's
209    * {@link #startObject()}, {@link #fieldName(String)}, {@link #string(String)},
210    * {@link #number(long)}, {@link #bool(boolean)}, {@link #nil()}, and {@link #binary(byte[])}
211    * methods.  Per-format encoding (quoting, indentation, newlines, etc.) is whatever this
212    * writer is configured for.
213    *
214    * <p>
215    * <b>Cursor positioning:</b> the writer must be in a state where a value emit is legal &mdash;
216    * at root, after a {@link #fieldName(String)} call, or inside an array.  Calling
217    * {@code object(...)} from inside an object body without a preceding field name throws
218    * {@link IllegalStateException}, same as calling {@link #string(String)} would.
219    *
220    * <p>
221    * <b>Default behavior:</b> the default implementation throws
222    * {@link UnsupportedOperationException}.  Format-specific token writers override this by
223    * delegating to {@link PojoWalker#walk(TokenWriter, Object)}.
224    *
225    * <p>
226    * <b>Swaps and {@code @Schema}:</b> this method is purely structural.  Object swaps and
227    * schema annotations are NOT applied at the token layer &mdash; use the POJO databind path on
228    * {@link Serializer#write(Object)} for those.
229    *
230    * @param value The value to emit.  May be <jk>null</jk>.
231    * @return This object.
232    * @throws IOException If the underlying stream fails.
233    */
234   default TokenWriter object(Object value) throws IOException {
235      throw new UnsupportedOperationException(
236         "object(Object) is not implemented for " + getClass().getName());
237   }
238
239   /**
240    * Implements {@link RecordWriter#write(Object)} by delegating to {@link #object(Object)}.
241    *
242    * @param value The value.  May be <jk>null</jk>.
243    * @return This object.
244    * @throws IOException If a problem occurred writing to the underlying stream.
245    */
246   @Override
247   default TokenWriter write(Object value) throws IOException {
248      object(value);
249      return this;
250   }
251
252   /**
253    * Emits a {@link TokenType#VALUE_BINARY} event.
254    *
255    * <p>
256    * Text formats normally encode the value as base64-encoded {@link TokenType#VALUE_STRING}; the
257    * exact encoding is format-defined.
258    *
259    * @param value The value.  May be <jk>null</jk>, in which case a {@link TokenType#VALUE_NULL}
260    *    event is emitted instead.
261    * @return This object.
262    * @throws IOException If a problem occurred writing to the underlying stream.
263    * @throws SerializeException If the format cannot represent this event in the current state.
264    */
265   TokenWriter binary(byte[] value) throws IOException;
266
267   /**
268    * Structural token cursors are always O(1)-memory streamers.
269    *
270    * @return Always <jk>true</jk>.
271    */
272   @Override
273   default boolean isStreaming() { return true; }
274
275   /**
276    * Flushes any buffered output to the underlying stream.
277    *
278    * @throws IOException If a problem occurred flushing the underlying stream.
279    */
280   @Override
281   void flush() throws IOException;
282
283   /**
284    * Closes the writer, flushing any buffered output first.
285    *
286    * <p>
287    * Implementations should be idempotent &mdash; closing a closed writer is a no-op.
288    */
289   @Override
290   void close() throws IOException;
291
292   // =================================================================================
293   // Binary-native opt-in emits.  These default to UnsupportedOperationException so
294   // formats override only the subset they support (CBOR overrides writeTag and
295   // writeSimple; MsgPack overrides writeExt; text formats inherit the throwing
296   // defaults).  See {@link BinaryNativeKind} for the catalog of native concepts.
297   // =================================================================================
298
299   /**
300    * Emits a semantic tag prefixing the next value emit.
301    *
302    * <p>
303    * Tags compose: {@code writeTag(1).writeTag(2).number(5)} writes {@code tag(1)(tag(2)(5))}.
304    * The tag header is written immediately and does NOT consume a map-key/value or array-element
305    * slot; that bookkeeping belongs to the wrapped value emit that follows.
306    *
307    * @param tagNumber The unsigned tag number (carried as a {@code long} for the full unsigned
308    *    64-bit range).
309    * @return This object.
310    * @throws IOException If a problem occurred writing to the underlying stream.
311    * @throws UnsupportedOperationException If the format does not support semantic tags.
312    */
313   default TokenWriter writeTag(long tagNumber) throws IOException {
314      throw new UnsupportedOperationException("writeTag is not supported by this writer.");
315   }
316
317   /**
318    * Emits an opaque simple value.  The corresponding read-side token is
319    * {@link TokenType#VALUE_NULL} with {@link TokenReader#getSimpleValue()}.
320    *
321    * @param value The simple value.  Format-specific reserved encodings (e.g. CBOR major-7
322    *    codepoints 20-23, 25-27, 31 collide with bool/null/undefined/float/break) must not be
323    *    used.
324    * @return This object.
325    * @throws IOException If a problem occurred writing to the underlying stream.
326    * @throws UnsupportedOperationException If the format does not support opaque simple values.
327    */
328   default TokenWriter writeSimple(int value) throws IOException {
329      throw new UnsupportedOperationException("writeSimple is not supported by this writer.");
330   }
331
332   /**
333    * Emits a typed-binary value (signed type byte + binary payload).  The corresponding
334    * read-side token is {@link TokenType#VALUE_BINARY} with {@link TokenReader#getExtType()}
335    * and {@link TokenReader#getBinary()}.
336    *
337    * @param type The signed type byte (range {@code -128..127}).
338    * @param payload The payload bytes.  Must not be {@code null}.
339    * @return This object.
340    * @throws IOException If a problem occurred writing to the underlying stream.
341    * @throws UnsupportedOperationException If the format does not support typed-binary values.
342    */
343   default TokenWriter writeExt(int type, byte[] payload) throws IOException {
344      throw new UnsupportedOperationException("writeExt is not supported by this writer.");
345   }
346}