1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.io;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20import static com.google.common.base.Preconditions.checkPositionIndexes;
21
22import com.google.common.annotations.Beta;
23import com.google.common.base.Charsets;
24import com.google.common.base.Function;
25import com.google.common.collect.Iterables;
26
27import java.io.Closeable;
28import java.io.EOFException;
29import java.io.IOException;
30import java.io.InputStream;
31import java.io.InputStreamReader;
32import java.io.OutputStream;
33import java.io.OutputStreamWriter;
34import java.io.Reader;
35import java.io.StringReader;
36import java.io.Writer;
37import java.nio.CharBuffer;
38import java.nio.charset.Charset;
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.List;
42
43/**
44 * Provides utility methods for working with character streams.
45 *
46 * <p>All method parameters must be non-null unless documented otherwise.
47 *
48 * <p>Some of the methods in this class take arguments with a generic type of
49 * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
50 * those interfaces. Similarly for {@code Appendable & Closeable} and
51 * {@link java.io.Writer}.
52 *
53 * @author Chris Nokleberg
54 * @author Bin Zhu
55 * @author Colin Decker
56 * @since 1.0
57 */
58@Beta
59public final class CharStreams {
60  private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
61
62  private CharStreams() {}
63
64  /**
65   * Returns a factory that will supply instances of {@link StringReader} that
66   * read a string value.
67   *
68   * @param value the string to read
69   * @return the factory
70   * @deprecated Use {@link CharSource#wrap(CharSequence)} instead. This method
71   *     is scheduled for removal in Guava 18.0.
72   */
73  @Deprecated
74  public static InputSupplier<StringReader> newReaderSupplier(
75      final String value) {
76    return asInputSupplier(CharSource.wrap(value));
77  }
78
79  /**
80   * Returns a {@link CharSource} that reads the given string value.
81   *
82   * @since 14.0
83   * @deprecated Use {@link CharSource#wrap(CharSequence)} instead. This method
84   *     is scheduled to be removed in Guava 16.0.
85   */
86  @Deprecated
87  public static CharSource asCharSource(String string) {
88    return CharSource.wrap(string);
89  }
90
91  /**
92   * Returns a factory that will supply instances of {@link InputStreamReader},
93   * using the given {@link InputStream} factory and character set.
94   *
95   * @param in the factory that will be used to open input streams
96   * @param charset the charset used to decode the input stream; see {@link
97   *     Charsets} for helpful predefined constants
98   * @return the factory
99   * @deprecated Use {@link ByteSource#asCharSource(Charset)} instead. This
100   *     method is scheduled for removal in Guava 18.0.
101   */
102  @Deprecated
103  public static InputSupplier<InputStreamReader> newReaderSupplier(
104      final InputSupplier<? extends InputStream> in, final Charset charset) {
105    return asInputSupplier(
106        ByteStreams.asByteSource(in).asCharSource(charset));
107  }
108
109  /**
110   * Returns a factory that will supply instances of {@link OutputStreamWriter},
111   * using the given {@link OutputStream} factory and character set.
112   *
113   * @param out the factory that will be used to open output streams
114   * @param charset the charset used to encode the output stream; see {@link
115   *     Charsets} for helpful predefined constants
116   * @return the factory
117   * @deprecated Use {@link ByteSink#asCharSink(Charset)} instead. This method
118   *     is scheduled for removal in Guava 18.0.
119   */
120  @Deprecated
121  public static OutputSupplier<OutputStreamWriter> newWriterSupplier(
122      final OutputSupplier<? extends OutputStream> out, final Charset charset) {
123    return asOutputSupplier(
124        ByteStreams.asByteSink(out).asCharSink(charset));
125  }
126
127  /**
128   * Writes a character sequence (such as a string) to an appendable
129   * object from the given supplier.
130   *
131   * @param from the character sequence to write
132   * @param to the output supplier
133   * @throws IOException if an I/O error occurs
134   * @deprecated Use {@link CharSink#write(CharSequence)} instead. This method
135   *     is scheduled for removal in Guava 18.0.
136   */
137  @Deprecated
138  public static <W extends Appendable & Closeable> void write(CharSequence from,
139      OutputSupplier<W> to) throws IOException {
140    asCharSink(to).write(from);
141  }
142
143  /**
144   * Opens {@link Readable} and {@link Appendable} objects from the
145   * given factories, copies all characters between the two, and closes
146   * them.
147   *
148   * @param from the input factory
149   * @param to the output factory
150   * @return the number of characters copied
151   * @throws IOException if an I/O error occurs
152   * @deprecated Use {@link CharSource#copyTo(CharSink)} instead. This method is
153   *     scheduled for removal in Guava 18.0.
154   */
155  @Deprecated
156  public static <R extends Readable & Closeable,
157      W extends Appendable & Closeable> long copy(InputSupplier<R> from,
158      OutputSupplier<W> to) throws IOException {
159    return asCharSource(from).copyTo(asCharSink(to));
160  }
161
162  /**
163   * Opens a {@link Readable} object from the supplier, copies all characters
164   * to the {@link Appendable} object, and closes the input. Does not close
165   * or flush the output.
166   *
167   * @param from the input factory
168   * @param to the object to write to
169   * @return the number of characters copied
170   * @throws IOException if an I/O error occurs
171   * @deprecated Use {@link CharSource#copyTo(Appendable)} instead. This method
172   *     is scheduled for removal in Guava 18.0.
173   */
174  @Deprecated
175  public static <R extends Readable & Closeable> long copy(
176      InputSupplier<R> from, Appendable to) throws IOException {
177    return asCharSource(from).copyTo(to);
178  }
179
180  /**
181   * Copies all characters between the {@link Readable} and {@link Appendable}
182   * objects. Does not close or flush either object.
183   *
184   * @param from the object to read from
185   * @param to the object to write to
186   * @return the number of characters copied
187   * @throws IOException if an I/O error occurs
188   */
189  public static long copy(Readable from, Appendable to) throws IOException {
190    checkNotNull(from);
191    checkNotNull(to);
192    CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
193    long total = 0;
194    while (from.read(buf) != -1) {
195      buf.flip();
196      to.append(buf);
197      total += buf.remaining();
198      buf.clear();
199    }
200    return total;
201  }
202
203  /**
204   * Reads all characters from a {@link Readable} object into a {@link String}.
205   * Does not close the {@code Readable}.
206   *
207   * @param r the object to read from
208   * @return a string containing all the characters
209   * @throws IOException if an I/O error occurs
210   */
211  public static String toString(Readable r) throws IOException {
212    return toStringBuilder(r).toString();
213  }
214
215  /**
216   * Returns the characters from a {@link Readable} & {@link Closeable} object
217   * supplied by a factory as a {@link String}.
218   *
219   * @param supplier the factory to read from
220   * @return a string containing all the characters
221   * @throws IOException if an I/O error occurs
222   * @deprecated Use {@link CharSource#read()} instead. This method is
223   *     scheduled for removal in Guava 18.0.
224   */
225  @Deprecated
226  public static <R extends Readable & Closeable> String toString(
227      InputSupplier<R> supplier) throws IOException {
228    return asCharSource(supplier).read();
229  }
230
231  /**
232   * Reads all characters from a {@link Readable} object into a new
233   * {@link StringBuilder} instance. Does not close the {@code Readable}.
234   *
235   * @param r the object to read from
236   * @return a {@link StringBuilder} containing all the characters
237   * @throws IOException if an I/O error occurs
238   */
239  private static StringBuilder toStringBuilder(Readable r) throws IOException {
240    StringBuilder sb = new StringBuilder();
241    copy(r, sb);
242    return sb;
243  }
244
245  /**
246   * Reads the first line from a {@link Readable} & {@link Closeable} object
247   * supplied by a factory. The line does not include line-termination
248   * characters, but does include other leading and trailing whitespace.
249   *
250   * @param supplier the factory to read from
251   * @return the first line, or null if the reader is empty
252   * @throws IOException if an I/O error occurs
253   * @deprecated Use {@link CharSource#readFirstLine()} instead. This method is
254   *     scheduled for removal in Guava 18.0.
255   */
256  @Deprecated
257  public static <R extends Readable & Closeable> String readFirstLine(
258      InputSupplier<R> supplier) throws IOException {
259    return asCharSource(supplier).readFirstLine();
260  }
261
262  /**
263   * Reads all of the lines from a {@link Readable} & {@link Closeable} object
264   * supplied by a factory. The lines do not include line-termination
265   * characters, but do include other leading and trailing whitespace.
266   *
267   * @param supplier the factory to read from
268   * @return a mutable {@link List} containing all the lines
269   * @throws IOException if an I/O error occurs
270   * @deprecated Use {@link CharSource#readLines()} instead, but note that it
271   *     returns an {@code ImmutableList}. This method is scheduled for removal
272   *     in Guava 18.0.
273   */
274  @Deprecated
275  public static <R extends Readable & Closeable> List<String> readLines(
276      InputSupplier<R> supplier) throws IOException {
277    Closer closer = Closer.create();
278    try {
279      R r = closer.register(supplier.getInput());
280      return readLines(r);
281    } catch (Throwable e) {
282      throw closer.rethrow(e);
283    } finally {
284      closer.close();
285    }
286  }
287
288  /**
289   * Reads all of the lines from a {@link Readable} object. The lines do
290   * not include line-termination characters, but do include other
291   * leading and trailing whitespace.
292   *
293   * <p>Does not close the {@code Readable}. If reading files or resources you
294   * should use the {@link Files#readLines} and {@link Resources#readLines}
295   * methods.
296   *
297   * @param r the object to read from
298   * @return a mutable {@link List} containing all the lines
299   * @throws IOException if an I/O error occurs
300   */
301  public static List<String> readLines(Readable r) throws IOException {
302    List<String> result = new ArrayList<String>();
303    LineReader lineReader = new LineReader(r);
304    String line;
305    while ((line = lineReader.readLine()) != null) {
306      result.add(line);
307    }
308    return result;
309  }
310
311  /**
312   * Streams lines from a {@link Readable} object, stopping when the processor
313   * returns {@code false} or all lines have been read and returning the result
314   * produced by the processor. Does not close {@code readable}. Note that this
315   * method may not fully consume the contents of {@code readable} if the
316   * processor stops processing early.
317   *
318   * @throws IOException if an I/O error occurs
319   * @since 14.0
320   */
321  public static <T> T readLines(
322      Readable readable, LineProcessor<T> processor) throws IOException {
323    checkNotNull(readable);
324    checkNotNull(processor);
325
326    LineReader lineReader = new LineReader(readable);
327    String line;
328    while ((line = lineReader.readLine()) != null) {
329      if (!processor.processLine(line)) {
330        break;
331      }
332    }
333    return processor.getResult();
334  }
335
336  /**
337   * Streams lines from a {@link Readable} and {@link Closeable} object
338   * supplied by a factory, stopping when our callback returns false, or we
339   * have read all of the lines.
340   *
341   * @param supplier the factory to read from
342   * @param callback the LineProcessor to use to handle the lines
343   * @return the output of processing the lines
344   * @throws IOException if an I/O error occurs
345   * @deprecated Use {@link CharSource#readLines(LineProcessor)} instead. This
346   *     method is scheduled for removal in Guava 18.0.
347   */
348  @Deprecated
349  public static <R extends Readable & Closeable, T> T readLines(
350      InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException {
351    checkNotNull(supplier);
352    checkNotNull(callback);
353
354    Closer closer = Closer.create();
355    try {
356      R r = closer.register(supplier.getInput());
357      return readLines(r, callback);
358    } catch (Throwable e) {
359      throw closer.rethrow(e);
360    } finally {
361      closer.close();
362    }
363  }
364
365  /**
366   * Joins multiple {@link Reader} suppliers into a single supplier.
367   * Reader returned from the supplier will contain the concatenated data
368   * from the readers of the underlying suppliers.
369   *
370   * <p>Reading from the joined reader will throw a {@link NullPointerException}
371   * if any of the suppliers are null or return null.
372   *
373   * <p>Only one underlying reader will be open at a time. Closing the
374   * joined reader will close the open underlying reader.
375   *
376   * @param suppliers the suppliers to concatenate
377   * @return a supplier that will return a reader containing the concatenated
378   *     data
379   * @deprecated Use {@link CharSource#concat(Iterable)} instead. This method
380   *     is scheduled for removal in Guava 18.0.
381   */
382  @Deprecated
383  public static InputSupplier<Reader> join(
384      final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
385    checkNotNull(suppliers);
386    Iterable<CharSource> sources = Iterables.transform(suppliers,
387        new Function<InputSupplier<? extends Reader>, CharSource>() {
388          @Override
389          public CharSource apply(InputSupplier<? extends Reader> input) {
390            return asCharSource(input);
391          }
392        });
393    return asInputSupplier(CharSource.concat(sources));
394  }
395
396  /**
397   * Varargs form of {@link #join(Iterable)}.
398   *
399   * @deprecated Use {@link CharSource#concat(CharSource[])} instead. This
400   *     method is scheduled for removal in Guava 18.0.
401   */
402  @Deprecated
403  @SuppressWarnings("unchecked") // suppress "possible heap pollution" warning in JDK7
404  public static InputSupplier<Reader> join(
405      InputSupplier<? extends Reader>... suppliers) {
406    return join(Arrays.asList(suppliers));
407  }
408
409  /**
410   * Discards {@code n} characters of data from the reader. This method
411   * will block until the full amount has been skipped. Does not close the
412   * reader.
413   *
414   * @param reader the reader to read from
415   * @param n the number of characters to skip
416   * @throws EOFException if this stream reaches the end before skipping all
417   *     the characters
418   * @throws IOException if an I/O error occurs
419   */
420  public static void skipFully(Reader reader, long n) throws IOException {
421    checkNotNull(reader);
422    while (n > 0) {
423      long amt = reader.skip(n);
424      if (amt == 0) {
425        // force a blocking read
426        if (reader.read() == -1) {
427          throw new EOFException();
428        }
429        n--;
430      } else {
431        n -= amt;
432      }
433    }
434  }
435
436  /**
437   * Returns a {@link Writer} that simply discards written chars.
438   *
439   * @since 15.0
440   */
441  public static Writer nullWriter() {
442    return NullWriter.INSTANCE;
443  }
444
445  private static final class NullWriter extends Writer {
446
447    private static final NullWriter INSTANCE = new NullWriter();
448
449    @Override
450    public void write(int c) {
451    }
452
453    @Override
454    public void write(char[] cbuf) {
455      checkNotNull(cbuf);
456    }
457
458    @Override
459    public void write(char[] cbuf, int off, int len) {
460      checkPositionIndexes(off, off + len, cbuf.length);
461    }
462
463    @Override
464    public void write(String str) {
465      checkNotNull(str);
466    }
467
468    @Override
469    public void write(String str, int off, int len) {
470      checkPositionIndexes(off, off + len, str.length());
471    }
472
473    @Override
474    public Writer append(CharSequence csq) {
475      checkNotNull(csq);
476      return this;
477    }
478
479    @Override
480    public Writer append(CharSequence csq, int start, int end) {
481      checkPositionIndexes(start, end, csq.length());
482      return this;
483    }
484
485    @Override
486    public Writer append(char c) {
487      return this;
488    }
489
490    @Override
491    public void flush() {
492    }
493
494    @Override
495    public void close() {
496    }
497
498    @Override
499    public String toString() {
500      return "CharStreams.nullWriter()";
501    }
502  }
503
504  /**
505   * Returns a Writer that sends all output to the given {@link Appendable}
506   * target. Closing the writer will close the target if it is {@link
507   * Closeable}, and flushing the writer will flush the target if it is {@link
508   * java.io.Flushable}.
509   *
510   * @param target the object to which output will be sent
511   * @return a new Writer object, unless target is a Writer, in which case the
512   *     target is returned
513   */
514  public static Writer asWriter(Appendable target) {
515    if (target instanceof Writer) {
516      return (Writer) target;
517    }
518    return new AppendableWriter(target);
519  }
520
521  // TODO(user): Remove these once Input/OutputSupplier methods are removed
522
523  static Reader asReader(final Readable readable) {
524    checkNotNull(readable);
525    if (readable instanceof Reader) {
526      return (Reader) readable;
527    }
528    return new Reader() {
529      @Override
530      public int read(char[] cbuf, int off, int len) throws IOException {
531        return read(CharBuffer.wrap(cbuf, off, len));
532      }
533
534      @Override
535      public int read(CharBuffer target) throws IOException {
536        return readable.read(target);
537      }
538
539      @Override
540      public void close() throws IOException {
541        if (readable instanceof Closeable) {
542          ((Closeable) readable).close();
543        }
544      }
545    };
546  }
547
548  /**
549   * Returns a view of the given {@code Readable} supplier as a
550   * {@code CharSource}.
551   *
552   * <p>This method is a temporary method provided for easing migration from
553   * suppliers to sources and sinks.
554   *
555   * @since 15.0
556   * @deprecated Convert all {@code InputSupplier<? extends Readable>}
557   *     implementations to extend {@link CharSource} or provide a method for
558   *     viewing the object as a {@code CharSource}. This method is scheduled
559   *     for removal in Guava 18.0.
560   */
561  @Deprecated
562  public static CharSource asCharSource(
563      final InputSupplier<? extends Readable> supplier) {
564    checkNotNull(supplier);
565    return new CharSource() {
566      @Override
567      public Reader openStream() throws IOException {
568        return asReader(supplier.getInput());
569      }
570
571      @Override
572      public String toString() {
573        return "CharStreams.asCharSource(" + supplier + ")";
574      }
575    };
576  }
577
578  /**
579   * Returns a view of the given {@code Appendable} supplier as a
580   * {@code CharSink}.
581   *
582   * <p>This method is a temporary method provided for easing migration from
583   * suppliers to sources and sinks.
584   *
585   * @since 15.0
586   * @deprecated Convert all {@code OutputSupplier<? extends Appendable>}
587   *     implementations to extend {@link CharSink} or provide a method for
588   *     viewing the object as a {@code CharSink}. This method is scheduled
589   *     for removal in Guava 18.0.
590   */
591  @Deprecated
592  public static CharSink asCharSink(
593      final OutputSupplier<? extends Appendable> supplier) {
594    checkNotNull(supplier);
595    return new CharSink() {
596      @Override
597      public Writer openStream() throws IOException {
598        return asWriter(supplier.getOutput());
599      }
600
601      @Override
602      public String toString() {
603        return "CharStreams.asCharSink(" + supplier + ")";
604      }
605    };
606  }
607
608  @SuppressWarnings("unchecked") // used internally where known to be safe
609  static <R extends Reader> InputSupplier<R> asInputSupplier(
610      CharSource source) {
611    return (InputSupplier) checkNotNull(source);
612  }
613
614  @SuppressWarnings("unchecked") // used internally where known to be safe
615  static <W extends Writer> OutputSupplier<W> asOutputSupplier(
616      CharSink sink) {
617    return (OutputSupplier) checkNotNull(sink);
618  }
619}
620