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.Preconditions;
021
022import java.io.Closeable;
023import java.io.EOFException;
024import java.io.IOException;
025import java.io.InputStream;
026import java.io.InputStreamReader;
027import java.io.OutputStream;
028import java.io.OutputStreamWriter;
029import java.io.Reader;
030import java.io.StringReader;
031import java.io.Writer;
032import java.nio.CharBuffer;
033import java.nio.charset.Charset;
034import java.util.ArrayList;
035import java.util.Arrays;
036import 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
051 */
052@Beta
053public 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}