001 /*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package com.google.common.io;
018
019 import com.google.common.annotations.Beta;
020 import com.google.common.base.Preconditions;
021
022 import java.io.Closeable;
023 import java.io.EOFException;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.io.InputStreamReader;
027 import java.io.OutputStream;
028 import java.io.OutputStreamWriter;
029 import java.io.Reader;
030 import java.io.StringReader;
031 import java.io.Writer;
032 import java.nio.CharBuffer;
033 import java.nio.charset.Charset;
034 import java.util.ArrayList;
035 import java.util.Arrays;
036 import java.util.List;
037
038 /**
039 * Provides utility methods for working with character streams.
040 *
041 * <p>All method parameters must be non-null unless documented otherwise.
042 *
043 * <p>Some of the methods in this class take arguments with a generic type of
044 * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
045 * those interfaces. Similarly for {@code Appendable & Closeable} and
046 * {@link java.io.Writer}.
047 *
048 * @author Chris Nokleberg
049 * @author Bin Zhu
050 * @since 1.0
051 */
052 @Beta
053 public final class CharStreams {
054 private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
055
056 private CharStreams() {}
057
058 /**
059 * Returns a factory that will supply instances of {@link StringReader} that
060 * read a string value.
061 *
062 * @param value the string to read
063 * @return the factory
064 */
065 public static InputSupplier<StringReader> newReaderSupplier(
066 final String value) {
067 Preconditions.checkNotNull(value);
068 return new InputSupplier<StringReader>() {
069 @Override
070 public StringReader getInput() {
071 return new StringReader(value);
072 }
073 };
074 }
075
076 /**
077 * Returns a factory that will supply instances of {@link InputStreamReader},
078 * using the given {@link InputStream} factory and character set.
079 *
080 * @param in the factory that will be used to open input streams
081 * @param charset the character set used to decode the input stream
082 * @return the factory
083 */
084 public static InputSupplier<InputStreamReader> newReaderSupplier(
085 final InputSupplier<? extends InputStream> in, final Charset charset) {
086 Preconditions.checkNotNull(in);
087 Preconditions.checkNotNull(charset);
088 return new InputSupplier<InputStreamReader>() {
089 @Override
090 public InputStreamReader getInput() throws IOException {
091 return new InputStreamReader(in.getInput(), charset);
092 }
093 };
094 }
095
096 /**
097 * Returns a factory that will supply instances of {@link OutputStreamWriter},
098 * using the given {@link OutputStream} factory and character set.
099 *
100 * @param out the factory that will be used to open output streams
101 * @param charset the character set used to encode the output stream
102 * @return the factory
103 */
104 public static OutputSupplier<OutputStreamWriter> newWriterSupplier(
105 final OutputSupplier<? extends OutputStream> out, final Charset charset) {
106 Preconditions.checkNotNull(out);
107 Preconditions.checkNotNull(charset);
108 return new OutputSupplier<OutputStreamWriter>() {
109 @Override
110 public OutputStreamWriter getOutput() throws IOException {
111 return new OutputStreamWriter(out.getOutput(), charset);
112 }
113 };
114 }
115
116 /**
117 * Writes a character sequence (such as a string) to an appendable
118 * object from the given supplier.
119 *
120 * @param from the character sequence to write
121 * @param to the output supplier
122 * @throws IOException if an I/O error occurs
123 */
124 public static <W extends Appendable & Closeable> void write(CharSequence from,
125 OutputSupplier<W> to) throws IOException {
126 Preconditions.checkNotNull(from);
127 boolean threw = true;
128 W out = to.getOutput();
129 try {
130 out.append(from);
131 threw = false;
132 } finally {
133 Closeables.close(out, threw);
134 }
135 }
136
137 /**
138 * Opens {@link Readable} and {@link Appendable} objects from the
139 * given factories, copies all characters between the two, and closes
140 * them.
141 *
142 * @param from the input factory
143 * @param to the output factory
144 * @return the number of characters copied
145 * @throws IOException if an I/O error occurs
146 */
147 public static <R extends Readable & Closeable,
148 W extends Appendable & Closeable> long copy(InputSupplier<R> from,
149 OutputSupplier<W> to) throws IOException {
150 boolean threw = true;
151 R in = from.getInput();
152 try {
153 W out = to.getOutput();
154 try {
155 long count = copy(in, out);
156 threw = false;
157 return count;
158 } finally {
159 Closeables.close(out, threw);
160 }
161 } finally {
162 Closeables.close(in, threw);
163 }
164 }
165
166 /**
167 * Opens a {@link Readable} object from the supplier, copies all characters
168 * to the {@link Appendable} object, and closes the input. Does not close
169 * or flush the output.
170 *
171 * @param from the input factory
172 * @param to the object to write to
173 * @return the number of characters copied
174 * @throws IOException if an I/O error occurs
175 */
176 public static <R extends Readable & Closeable> long copy(
177 InputSupplier<R> from, Appendable to) throws IOException {
178 boolean threw = true;
179 R in = from.getInput();
180 try {
181 long count = copy(in, to);
182 threw = false;
183 return count;
184 } finally {
185 Closeables.close(in, threw);
186 }
187 }
188
189 /**
190 * Copies all characters between the {@link Readable} and {@link Appendable}
191 * objects. Does not close or flush either object.
192 *
193 * @param from the object to read from
194 * @param to the object to write to
195 * @return the number of characters copied
196 * @throws IOException if an I/O error occurs
197 */
198 public static long copy(Readable from, Appendable to) throws IOException {
199 CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
200 long total = 0;
201 while (true) {
202 int r = from.read(buf);
203 if (r == -1) {
204 break;
205 }
206 buf.flip();
207 to.append(buf, 0, r);
208 total += r;
209 }
210 return total;
211 }
212
213 /**
214 * Reads all characters from a {@link Readable} object into a {@link String}.
215 * Does not close the {@code Readable}.
216 *
217 * @param r the object to read from
218 * @return a string containing all the characters
219 * @throws IOException if an I/O error occurs
220 */
221 public static String toString(Readable r) throws IOException {
222 return toStringBuilder(r).toString();
223 }
224
225 /**
226 * Returns the characters from a {@link Readable} & {@link Closeable} object
227 * supplied by a factory as a {@link String}.
228 *
229 * @param supplier the factory to read from
230 * @return a string containing all the characters
231 * @throws IOException if an I/O error occurs
232 */
233 public static <R extends Readable & Closeable> String toString(
234 InputSupplier<R> supplier) throws IOException {
235 return toStringBuilder(supplier).toString();
236 }
237
238 /**
239 * Reads all characters from a {@link Readable} object into a new
240 * {@link StringBuilder} instance. Does not close the {@code Readable}.
241 *
242 * @param r the object to read from
243 * @return a {@link StringBuilder} containing all the characters
244 * @throws IOException if an I/O error occurs
245 */
246 private static StringBuilder toStringBuilder(Readable r) throws IOException {
247 StringBuilder sb = new StringBuilder();
248 copy(r, sb);
249 return sb;
250 }
251
252 /**
253 * Returns the characters from a {@link Readable} & {@link Closeable} object
254 * supplied by a factory as a new {@link StringBuilder} instance.
255 *
256 * @param supplier the factory to read from
257 * @throws IOException if an I/O error occurs
258 */
259 private static <R extends Readable & Closeable> StringBuilder toStringBuilder(
260 InputSupplier<R> supplier) throws IOException {
261 boolean threw = true;
262 R r = supplier.getInput();
263 try {
264 StringBuilder result = toStringBuilder(r);
265 threw = false;
266 return result;
267 } finally {
268 Closeables.close(r, threw);
269 }
270 }
271
272 /**
273 * Reads the first line from a {@link Readable} & {@link Closeable} object
274 * supplied by a factory. The line does not include line-termination
275 * characters, but does include other leading and trailing whitespace.
276 *
277 * @param supplier the factory to read from
278 * @return the first line, or null if the reader is empty
279 * @throws IOException if an I/O error occurs
280 */
281 public static <R extends Readable & Closeable> String readFirstLine(
282 InputSupplier<R> supplier) throws IOException {
283 boolean threw = true;
284 R r = supplier.getInput();
285 try {
286 String line = new LineReader(r).readLine();
287 threw = false;
288 return line;
289 } finally {
290 Closeables.close(r, threw);
291 }
292 }
293
294 /**
295 * Reads all of the lines from a {@link Readable} & {@link Closeable} object
296 * supplied by a factory. The lines do not include line-termination
297 * characters, but do include other leading and trailing whitespace.
298 *
299 * @param supplier the factory to read from
300 * @return a mutable {@link List} containing all the lines
301 * @throws IOException if an I/O error occurs
302 */
303 public static <R extends Readable & Closeable> List<String> readLines(
304 InputSupplier<R> supplier) throws IOException {
305 boolean threw = true;
306 R r = supplier.getInput();
307 try {
308 List<String> result = readLines(r);
309 threw = false;
310 return result;
311 } finally {
312 Closeables.close(r, threw);
313 }
314 }
315
316 /**
317 * Reads all of the lines from a {@link Readable} object. The lines do
318 * not include line-termination characters, but do include other
319 * leading and trailing whitespace.
320 *
321 * <p>Does not close the {@code Readable}. If reading files or resources you
322 * should use the {@link Files#readLines} and {@link Resources#readLines}
323 * methods.
324 *
325 * @param r the object to read from
326 * @return a mutable {@link List} containing all the lines
327 * @throws IOException if an I/O error occurs
328 */
329 public static List<String> readLines(Readable r) throws IOException {
330 List<String> result = new ArrayList<String>();
331 LineReader lineReader = new LineReader(r);
332 String line;
333 while ((line = lineReader.readLine()) != null) {
334 result.add(line);
335 }
336 return result;
337 }
338
339 /**
340 * Streams lines from a {@link Readable} and {@link Closeable} object
341 * supplied by a factory, stopping when our callback returns false, or we
342 * have read all of the lines.
343 *
344 * @param supplier the factory to read from
345 * @param callback the LineProcessor to use to handle the lines
346 * @return the output of processing the lines
347 * @throws IOException if an I/O error occurs
348 */
349 public static <R extends Readable & Closeable, T> T readLines(
350 InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException {
351 boolean threw = true;
352 R r = supplier.getInput();
353 try {
354 LineReader lineReader = new LineReader(r);
355 String line;
356 while ((line = lineReader.readLine()) != null) {
357 if (!callback.processLine(line)) {
358 break;
359 }
360 }
361 threw = false;
362 } finally {
363 Closeables.close(r, threw);
364 }
365 return callback.getResult();
366 }
367
368 /**
369 * Joins multiple {@link Reader} suppliers into a single supplier.
370 * Reader returned from the supplier will contain the concatenated data
371 * from the readers of the underlying suppliers.
372 *
373 * <p>Reading from the joined reader will throw a {@link NullPointerException}
374 * if any of the suppliers are null or return null.
375 *
376 * <p>Only one underlying reader will be open at a time. Closing the
377 * joined reader will close the open underlying reader.
378 *
379 * @param suppliers the suppliers to concatenate
380 * @return a supplier that will return a reader containing the concatenated
381 * data
382 */
383 public static InputSupplier<Reader> join(
384 final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
385 return new InputSupplier<Reader>() {
386 @Override public Reader getInput() throws IOException {
387 return new MultiReader(suppliers.iterator());
388 }
389 };
390 }
391
392 /** Varargs form of {@link #join(Iterable)}. */
393 public static InputSupplier<Reader> join(
394 InputSupplier<? extends Reader>... suppliers) {
395 return join(Arrays.asList(suppliers));
396 }
397
398 /**
399 * Discards {@code n} characters of data from the reader. This method
400 * will block until the full amount has been skipped. Does not close the
401 * reader.
402 *
403 * @param reader the reader to read from
404 * @param n the number of characters to skip
405 * @throws EOFException if this stream reaches the end before skipping all
406 * the bytes
407 * @throws IOException if an I/O error occurs
408 */
409 public static void skipFully(Reader reader, long n) throws IOException {
410 while (n > 0) {
411 long amt = reader.skip(n);
412 if (amt == 0) {
413 // force a blocking read
414 if (reader.read() == -1) {
415 throw new EOFException();
416 }
417 n--;
418 } else {
419 n -= amt;
420 }
421 }
422 }
423
424 /**
425 * Returns a Writer that sends all output to the given {@link Appendable}
426 * target. Closing the writer will close the target if it is {@link
427 * Closeable}, and flushing the writer will flush the target if it is {@link
428 * java.io.Flushable}.
429 *
430 * @param target the object to which output will be sent
431 * @return a new Writer object, unless target is a Writer, in which case the
432 * target is returned
433 */
434 public static Writer asWriter(Appendable target) {
435 if (target instanceof Writer) {
436 return (Writer) target;
437 }
438 return new AppendableWriter(target);
439 }
440 }