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.lang.reflect.*;
021import java.util.*;
022import java.util.stream.*;
023
024import org.apache.juneau.marshall.*;
025import org.apache.juneau.marshall.parser.*;
026
027/**
028 * Public low-level whole-value pull cursor over a Juneau-formatted document.
029 *
030 * <p>
031 * Format-neutral cursor that yields one whole-value record per {@link #read(Class)} call.  Single-rooted
032 * formats (json, xml, bson, yaml, ...) degenerate to a one-record stream; multi-rooted formats
033 * (jsonl, csv, sse) expose each line / row / event as a separate record.
034 *
035 * <h5 class='section'>Usage:</h5>
036 * <p class='bjava'>
037 *    <jk>try</jk> (RecordReader <jv>r</jv> = Json.<jsf>DEFAULT</jsf>.readRecords(<jv>input</jv>)) {
038 *       <jk>while</jk> (<jv>r</jv>.canRead()) {
039 *          Bean <jv>bean</jv> = <jv>r</jv>.read(Bean.<jk>class</jk>);
040 *          process(<jv>bean</jv>);
041 *       }
042 *    }
043 * </p>
044 *
045 * <h5 class='section'>Streaming quality:</h5>
046 * <ul>
047 *    <li>{@link #isStreaming()} == <jk>true</jk> &mdash; O(1)-memory streaming.  FULL-token formats
048 *       return a cursor that is also a {@link TokenReader} (the structural surface).
049 *    <li>{@link #isStreaming()} == <jk>false</jk> &mdash; buffered whole-value cursor (backed by
050 *       {@link RecordAdapter}); {@link #read(Class)} delegates to the format's existing parser and
051 *       the structural surface is not available.
052 * </ul>
053 *
054 * <h5 class='section'>See Also:</h5><ul>
055 *    <li>{@link RecordWriter} &mdash; the push-side counterpart.
056 *    <li>{@link TokenReader} &mdash; structural extension for FULL-capability formats.
057 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
058 * </ul>
059 */
060public interface RecordReader extends Closeable {
061
062   /**
063    * Returns <jk>true</jk> if {@link #read(Class)} can be called to produce another record.
064    *
065    * <p>
066    * Single-rooted formats return <jk>true</jk> exactly once (for the root value), then
067    * <jk>false</jk> after the value has been consumed.  Multi-rooted formats return <jk>true</jk>
068    * once per remaining record on the wire.
069    *
070    * @return <jk>true</jk> if a record is available; <jk>false</jk> at end of stream.
071    * @throws IOException If a problem occurred peeking at the underlying stream.
072    * @throws ParseException If the input is malformed for the format.
073    */
074   boolean canRead() throws IOException, ParseException;
075
076   /**
077    * Reads and returns the next record bound to the requested type.
078    *
079    * @param <T> The expected return type.
080    * @param type The target Java type.
081    * @return The bound POJO.
082    * @throws IOException If a problem occurred reading from the underlying stream.
083    * @throws ParseException If the input is malformed for the format or cannot be bound to the type.
084    * @throws IllegalStateException If {@link #canRead()} returned <jk>false</jk>.
085    */
086   <T> T read(Class<T> type) throws IOException, ParseException;
087
088   /**
089    * Same as {@link #read(Class)} but accepts a pre-resolved {@link ClassMeta}.
090    *
091    * @param <T> The expected return type.
092    * @param type The target type.
093    * @return The bound POJO.
094    * @throws IOException If a problem occurred reading from the underlying stream.
095    * @throws ParseException If the input is malformed or cannot be bound to the type.
096    */
097   <T> T read(ClassMeta<T> type) throws IOException, ParseException;
098
099   /**
100    * Same as {@link #read(Class)} but accepts a parameterized {@link Type} and optional type
101    * arguments (the same shape as
102    * {@link Parser#read(Object, Type, Type...)}).
103    *
104    * @param <T> The expected return type.
105    * @param type The target type.
106    * @param args Optional type arguments for parameterized types.
107    * @return The bound POJO.
108    * @throws IOException If a problem occurred reading from the underlying stream.
109    * @throws ParseException If the input is malformed or cannot be bound to the type.
110    */
111   <T> T read(Type type, Type... args) throws IOException, ParseException;
112
113   /**
114    * Returns an {@link Iterator} view that yields each remaining record bound to the requested
115    * type.
116    *
117    * <p>
118    * The iterator drains from this cursor; consuming both surfaces concurrently is not supported.
119    *
120    * <p>
121    * Checked {@link IOException} / {@link ParseException} thrown by the underlying cursor are
122    * wrapped in an {@link RuntimeParseException} (carrying the typed cause) so they can surface
123    * through the {@link Iterator} API.
124    *
125    * @param <T> The element type.
126    * @param type The element type.
127    * @return An iterator over the remaining records.
128    */
129   default <T> Iterator<T> iterator(Class<T> type) {
130      return new Iterator<>() {
131         @Override public boolean hasNext() {
132            try {
133               return canRead();
134            } catch (IOException | ParseException e) {
135               throw new RuntimeParseException(e);
136            }
137         }
138         @Override public T next() {
139            if (! hasNext())
140               throw new NoSuchElementException();
141            try {
142               return read(type);
143            } catch (IOException | ParseException e) {
144               throw new RuntimeParseException(e);
145            }
146         }
147      };
148   }
149
150   /**
151    * Returns a {@link Stream} view that yields each remaining record bound to the requested type.
152    *
153    * @param <T> The element type.
154    * @param type The element type.
155    * @return A stream over the remaining records.
156    */
157   default <T> Stream<T> stream(Class<T> type) {
158      var spliterator = Spliterators.spliteratorUnknownSize(iterator(type), Spliterator.ORDERED);
159      return StreamSupport.stream(spliterator, false);
160   }
161
162   /**
163    * Returns <jk>true</jk> if this cursor is an O(1)-memory streaming cursor, or <jk>false</jk> if
164    * it is a buffered whole-value cursor (backed by {@link RecordAdapter}).
165    *
166    * @return <jk>true</jk> if this cursor streams; <jk>false</jk> if it buffers.
167    */
168   boolean isStreaming();
169
170   /**
171    * Closes the underlying stream.
172    *
173    * <p>
174    * Implementations should be idempotent &mdash; closing a closed reader is a no-op.
175    */
176   @Override
177   void close() throws IOException;
178}