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 static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.io.*; 022import java.lang.reflect.*; 023 024import org.apache.juneau.marshall.*; 025import org.apache.juneau.marshall.parser.*; 026 027/** 028 * Public low-level pull-parser cursor over a Juneau-formatted document. 029 * 030 * <p> 031 * A StAX-like enum cursor that emits a stream of {@link TokenType} events without materializing 032 * the whole object graph. This is a format-neutral surface; the same cursor methods work across 033 * every Juneau format that implements {@link TokenReadable} (the structural-token role). 034 * 035 * <h5 class='section'>Usage:</h5> 036 * <p class='bjava'> 037 * <jc>// Tokenize a JSON document without materializing it.</jc> 038 * <jk>try</jk> (TokenReader <jv>r</jv> = Json.<jsf>DEFAULT</jsf>.readTokens(<js>"{\"a\":1,\"b\":[true,null]}"</js>)) { 039 * <jk>while</jk> (<jv>r</jv>.next() != TokenType.<jsf>END_OF_STREAM</jsf>) { 040 * <jk>switch</jk> (<jv>r</jv>.getCurrentToken()) { 041 * <jk>case</jk> <jsf>FIELD_NAME</jsf> -> handleKey(<jv>r</jv>.getFieldName()); 042 * <jk>case</jk> <jsf>VALUE_STRING</jsf> -> handleString(<jv>r</jv>.getString()); 043 * <jk>case</jk> <jsf>VALUE_NUMBER</jsf> -> handleNumber(<jv>r</jv>.getNumber()); 044 * <jk>default</jk> -> { <jc>// structural </jc>} 045 * } 046 * } 047 * } 048 * </p> 049 * 050 * <h5 class='section'>Contract:</h5> 051 * <ul> 052 * <li>The cursor is <b>purely structural</b>. It bypasses object swaps and {@code @Schema} 053 * annotations — that is the role of the POJO databind path on 054 * {@link Parser Parser} and its bridges. 055 * <li>For numeric values both the original lexeme and a parsed {@link Number} are available 056 * (see {@link #getNumberLexeme()} and {@link #getNumber()}); precision-sensitive 057 * consumers should prefer the lexeme. 058 * <li>Concrete implementations are not required to be thread-safe. 059 * </ul> 060 * 061 * <h5 class='section'>See Also:</h5><ul> 062 * <li>{@link TokenWriter} — the push-side counterpart. 063 * <li>{@link TokenType} — the format-neutral token vocabulary. 064 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 065 * </ul> 066 */ 067public interface TokenReader extends RecordReader { 068 069 /** 070 * Canonical message thrown as an {@link IllegalStateException} when {@link #read(Class)} is 071 * called while the cursor is not positioned at a value boundary. 072 * 073 * <p> 074 * Shared across all formats so the positioning/error contract is uniform (see the 075 * <c>read</c> contract); implementations throw <c><jk>new</jk> IllegalStateException( 076 * {@link #MSG_READ_NON_VALUE_STATE})</c> rather than inventing their own wording. 077 */ 078 String MSG_READ_NON_VALUE_STATE = 079 "read called from a non-value cursor state. The cursor must be positioned such that next() " 080 + "would emit START_OBJECT / START_ARRAY / VALUE_*; the current state would emit a structural " 081 + "separator, field name, end-container, or end-of-stream instead."; 082 083 /** 084 * Advances the cursor to the next token. 085 * 086 * @return The token type of the new current token, or {@link TokenType#END_OF_STREAM} when 087 * the underlying input is exhausted. Never returns {@link TokenType#NOT_AVAILABLE} — 088 * that value is reserved for the initial pre-{@code next()} state. 089 * @throws IOException If a problem occurred reading from the underlying stream. 090 * @throws ParseException If the input is malformed for the format. 091 */ 092 TokenType next() throws IOException, ParseException; 093 094 /** 095 * Returns the current token type without advancing. 096 * 097 * @return The current token type. Returns {@link TokenType#NOT_AVAILABLE} before the first 098 * call to {@link #next()}, and {@link TokenType#END_OF_STREAM} once the stream is exhausted. 099 */ 100 TokenType getCurrentToken(); 101 102 /** 103 * Returns the current depth of the cursor. 104 * 105 * <p> 106 * Depth starts at <c>0</c> at the document root and increases by 1 for each 107 * {@link TokenType#START_OBJECT} / {@link TokenType#START_ARRAY} encountered (and decreases on 108 * the matching end tokens). 109 * 110 * @return The current depth. 111 */ 112 int getDepth(); 113 114 /** 115 * Returns the field name when the current token is {@link TokenType#FIELD_NAME}. 116 * 117 * @return The field name. 118 * @throws IllegalStateException If the current token is not {@link TokenType#FIELD_NAME}. 119 */ 120 String getFieldName(); 121 122 /** 123 * Returns the current scalar value as a string. 124 * 125 * <p> 126 * Defined for {@link TokenType#VALUE_STRING} (the natural value), {@link TokenType#FIELD_NAME} 127 * (returns the field name), {@link TokenType#VALUE_NUMBER} (returns the lexeme), and 128 * {@link TokenType#VALUE_BOOLEAN} (returns <js>"true"</js>/<js>"false"</js>). For 129 * {@link TokenType#VALUE_NULL} returns <jk>null</jk>. 130 * 131 * @return The string view of the current value. 132 * @throws IllegalStateException If the current token has no string view. 133 */ 134 String getString(); 135 136 /** 137 * Returns the current numeric value as a parsed {@link Number}. 138 * 139 * <p> 140 * Implementations may parse lazily; the returned {@link Number} subtype is implementation- and 141 * format-defined (e.g. {@link Long} / {@link Double} / {@link java.math.BigDecimal}). Use 142 * {@link #getNumberLexeme()} to recover the original textual form. 143 * 144 * @return The parsed numeric value. 145 * @throws IllegalStateException If the current token is not {@link TokenType#VALUE_NUMBER}. 146 * @throws ParseException If the lexeme cannot be parsed. 147 */ 148 Number getNumber() throws ParseException; 149 150 /** 151 * Returns the original numeric lexeme for the current numeric token. 152 * 153 * <p> 154 * For text formats this is the exact substring that produced the number. For binary formats 155 * the implementation returns a canonical text rendering of the native numeric token. 156 * 157 * @return The numeric lexeme. 158 * @throws IllegalStateException If the current token is not {@link TokenType#VALUE_NUMBER}. 159 */ 160 String getNumberLexeme(); 161 162 /** 163 * Returns the current boolean value. 164 * 165 * @return The boolean value. 166 * @throws IllegalStateException If the current token is not {@link TokenType#VALUE_BOOLEAN}. 167 */ 168 boolean getBool(); 169 170 /** 171 * Returns the current binary value. 172 * 173 * <p> 174 * Binary scalars come from binary formats only (cbor, msgpack, bson, ...). Text formats 175 * deliver embedded binary content as base64-encoded {@link TokenType#VALUE_STRING}. 176 * 177 * @return The binary value. 178 * @throws IllegalStateException If the current token is not {@link TokenType#VALUE_BINARY}. 179 */ 180 byte[] getBinary(); 181 182 /** 183 * Returns <jk>true</jk> if {@link #read(Class)} is currently callable on this cursor — 184 * i.e. the cursor is positioned at a value boundary. 185 * 186 * <p> 187 * This is the natural loop predicate when streaming records: 188 * <p class='bjava'> 189 * <jk>try</jk> (TokenReader <jv>r</jv> = Json.<jsf>DEFAULT</jsf>.readTokens(<jv>input</jv>)) { 190 * <jv>r</jv>.next(); <jc>// consume the opening [ </jc> 191 * <jk>while</jk> (<jv>r</jv>.canRead()) { 192 * Item <jv>item</jv> = <jv>r</jv>.read(Item.<jk>class</jk>); 193 * process(<jv>item</jv>); 194 * } 195 * <jv>r</jv>.next(); <jc>// consume the closing ]</jc> 196 * } 197 * </p> 198 * 199 * @return <jk>true</jk> if the next operation can be {@code read}; <jk>false</jk> if the 200 * cursor is positioned at a structural separator, field name, end-container, or end-of-stream. 201 * @throws IOException If a problem occurred peeking at the underlying stream. 202 * @throws ParseException If the input is malformed for the format. 203 */ 204 default boolean canRead() throws IOException, ParseException { 205 return false; 206 } 207 208 /** 209 * Consumes the upcoming value (object, array, or scalar) and binds it to a POJO via the 210 * format's existing parser. 211 * 212 * <p> 213 * <b>Cursor positioning:</b> the cursor must be in a state where the next call to {@link #next()} 214 * would emit a value-shaped token (i.e. {@link TokenType#START_OBJECT}, {@link TokenType#START_ARRAY}, 215 * or one of the {@code VALUE_*} tokens). Calling {@code read} consumes that upcoming value 216 * wholesale and advances the cursor's depth back to where it was before the value would have 217 * started — structurally equivalent to calling {@link #next()} on a scalar, or 218 * {@link #next()} + {@link #skipChildren()} on a container. 219 * 220 * <p> 221 * <b>Do NOT call {@link #next()} before {@code read}.</b> Once the structural opener 222 * (e.g. <c>{</c> or <c>[</c>) has been consumed by {@link #next()}, the underlying parser cannot 223 * re-bind the value. The contract is "instead-of-next, not after-next." 224 * 225 * <p> 226 * <b>Default behavior:</b> the default implementation throws 227 * {@link UnsupportedOperationException}. Format-specific token readers override this by 228 * delegating to their existing {@code *ParserSession} at the cursor's current input position 229 * and resyncing the cursor's state machine afterward. 230 * 231 * @param <T> The expected return type. 232 * @param type The target Java type. 233 * @return The bound POJO. 234 * @throws IOException If a problem occurred reading from the underlying stream. 235 * @throws ParseException If the input is malformed for the format or cannot be bound to the type. 236 * @throws IllegalStateException If the cursor is not positioned at a value boundary. 237 * @throws UnsupportedOperationException If this format's reader has not implemented the bridge 238 * (e.g. writer-only formats with no parser, or formats whose Phase-2 increment did not include 239 * the bridge). 240 */ 241 default <T> T read(Class<T> type) throws IOException, ParseException { 242 throw new UnsupportedOperationException( 243 "read is not implemented for " + getClass().getName()); 244 } 245 246 /** 247 * Same as {@link #read(Class)} but accepts a pre-resolved {@link ClassMeta}. 248 * 249 * @param <T> The expected return type. 250 * @param type The target type. 251 * @return The bound POJO. 252 * @throws IOException If a problem occurred reading from the underlying stream. 253 * @throws ParseException If the input is malformed or cannot be bound to the type. 254 */ 255 default <T> T read(ClassMeta<T> type) throws IOException, ParseException { 256 throw new UnsupportedOperationException( 257 "read is not implemented for " + getClass().getName()); 258 } 259 260 /** 261 * Same as {@link #read(Class)} but accepts a parameterized {@link Type} and optional type 262 * arguments (the same shape as {@link Parser#read(Object, Type, Type...)}). 263 * 264 * @param <T> The expected return type. 265 * @param type The target type. 266 * @param args Optional type arguments for parameterized types. 267 * @return The bound POJO. 268 * @throws IOException If a problem occurred reading from the underlying stream. 269 * @throws ParseException If the input is malformed or cannot be bound to the type. 270 */ 271 default <T> T read(Type type, Type... args) throws IOException, ParseException { 272 throw new UnsupportedOperationException( 273 "read is not implemented for " + getClass().getName()); 274 } 275 276 /** 277 * Skips over the children of the current structural token. 278 * 279 * <p> 280 * If the current token is {@link TokenType#START_OBJECT} or {@link TokenType#START_ARRAY}, 281 * the cursor is advanced to the matching {@link TokenType#END_OBJECT} or 282 * {@link TokenType#END_ARRAY} (i.e. the entire sub-tree is consumed). For any other token this 283 * is a no-op. 284 * 285 * @throws IOException If a problem occurred reading from the underlying stream. 286 * @throws ParseException If the input is malformed for the format. 287 */ 288 void skipChildren() throws IOException, ParseException; 289 290 /** 291 * Structural token cursors are always O(1)-memory streamers. 292 * 293 * @return Always <jk>true</jk>. 294 */ 295 @Override 296 default boolean isStreaming() { return true; } 297 298 /** 299 * Closes the underlying stream. 300 * 301 * <p> 302 * Implementations should be idempotent — closing a closed reader is a no-op. 303 */ 304 @Override 305 void close() throws IOException; 306 307 // ================================================================================= 308 // Binary-native opt-in metadata accessors. Default cursors and formats without 309 // native concepts inherit the safe defaults below: getNativeKind() returns NONE, 310 // getTagCount() returns 0, and the value accessors throw. Format-specific cursors 311 // override the relevant subset (CBOR overrides getNativeKind/getTagCount/getTag/ 312 // getSimpleValue; MsgPack overrides getNativeKind/getExtType). See 313 // {@link BinaryNativeKind} for the catalog of native concepts. 314 // ================================================================================= 315 316 /** 317 * Returns the binary-native classification of the current token. 318 * 319 * @return The native kind, or {@link BinaryNativeKind#NONE NONE} when the current token 320 * carries no binary-native metadata. Never {@code null}. 321 */ 322 default BinaryNativeKind getNativeKind() { 323 return BinaryNativeKind.NONE; 324 } 325 326 /** 327 * Returns the number of semantic tags wrapping the current token. 328 * 329 * @return The tag count (0 when {@link #getNativeKind()} is not 330 * {@link BinaryNativeKind#CBOR_TAG CBOR_TAG}). 331 */ 332 default int getTagCount() { 333 return 0; 334 } 335 336 /** 337 * Returns the semantic tag at the specified position in the tag stack. 338 * 339 * @param index The tag position; outermost tag is {@code 0}. 340 * @return The tag number. 341 * @throws IndexOutOfBoundsException If {@code index} is outside {@code [0, getTagCount())}. 342 */ 343 default long getTag(int index) { 344 throw new IndexOutOfBoundsException("Tag index " + index + " out of bounds for tagCount " + getTagCount()); 345 } 346 347 /** 348 * Returns the typed-binary type byte for the current token. 349 * 350 * @return The signed type byte (range {@code -128..127}). 351 * @throws IllegalStateException If {@link #getNativeKind()} is not 352 * {@link BinaryNativeKind#MSGPACK_EXT MSGPACK_EXT}. 353 */ 354 default int getExtType() { 355 throw isex("Current token is not a typed-binary (ext) value (nativeKind=%s)", getNativeKind()); 356 } 357 358 /** 359 * Returns the opaque simple value for the current token. 360 * 361 * @return The simple int. 362 * @throws IllegalStateException If {@link #getNativeKind()} is not 363 * {@link BinaryNativeKind#CBOR_SIMPLE CBOR_SIMPLE}. 364 */ 365 default int getSimpleValue() { 366 throw isex("Current token is not a simple value (nativeKind=%s)", getNativeKind()); 367 } 368}