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