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.jsonl;
018
019import java.io.*;
020
021import org.apache.juneau.marshall.json.*;
022import org.apache.juneau.marshall.parser.*;
023import org.apache.juneau.marshall.stream.*;
024
025/**
026 * Reference implementation of the public {@link TokenReader} surface for the JSONL / NDJSON
027 * format.
028 *
029 * <p>
030 * JSONL is a sequence of independent JSON values separated by newlines.  There is no root
031 * container.  This cursor models that as a flat top-level sequence at {@link #getDepth() depth} 0:
032 * each line's tokens are emitted in turn, and {@link #next()} returns {@link TokenType#END_OF_STREAM}
033 * only when the underlying input is exhausted.
034 *
035 * <p>
036 * The most ergonomic way to consume a JSONL stream is via {@link #read(Class)}, which binds one
037 * line to a POJO per call:
038 * <p class='bjava'>
039 *    <jk>try</jk> (TokenReader <jv>r</jv> = Jsonl.<jsf>DEFAULT</jsf>.readTokens(<jv>reader</jv>)) {
040 *       <jk>while</jk> (<jv>r</jv>.canRead()) {
041 *          MyRecord <jv>rec</jv> = <jv>r</jv>.read(MyRecord.<jk>class</jk>);
042 *          process(<jv>rec</jv>);
043 *       }
044 *    }
045 * </p>
046 *
047 * <h5 class='section'>Notes:</h5><ul>
048 *    <li class='warn'>This class is not thread safe.
049 *    <li>The cursor is a true O(1)-memory streaming cursor ({@link #isStreaming()} == <jk>true</jk>).
050 * </ul>
051 */
052public class JsonlTokenReader extends JsonTokenReader {
053
054   /**
055    * Constructor with default settings.
056    *
057    * @param pipe The parser input pipe to read from.  Must not be <jk>null</jk>.
058    * @throws IOException If a problem occurred opening the underlying reader.
059    */
060   public JsonlTokenReader(ParserPipe pipe) throws IOException {
061      super(pipe);
062   }
063
064   /**
065    * Constructor used by {@link JsonlParserSession#readTokens(Object)} to plumb the calling
066    * session through.
067    *
068    * @param pipe The parser input pipe.  Must not be <jk>null</jk>.
069    * @param settings The cursor-level settings.  Must not be <jk>null</jk>.
070    * @param session The {@link JsonlParserSession} for {@link #read(Class)} delegation, or
071    *    <jk>null</jk> to disable {@code read}.
072    * @throws IOException If a problem occurred opening the underlying reader.
073    */
074   public JsonlTokenReader(ParserPipe pipe, Settings settings, JsonlParserSession session) throws IOException {
075      super(pipe, settings, session);
076   }
077
078   @Override /* JsonTokenReader */
079   protected void afterValue() {
080      // At depth 0, a top-level value just completed and we want the next next() / read()
081      // to consume the next line's value (instead of transitioning to S05_end as plain JSON
082      // would).  Inside a container the parent's logic (S03_expectCommaOrEndObject /
083      // S04_expectCommaOrEndArray) is still correct.
084      if (depth == 0)
085         state = S00_expectValue;
086      else
087         super.afterValue();
088   }
089}