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> — 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> — 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} — the push-side counterpart. 056 * <li>{@link TokenReader} — 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 — closing a closed reader is a no-op. 175 */ 176 @Override 177 void close() throws IOException; 178}