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 com.google.common.annotations.Beta;
20import com.google.common.base.Preconditions;
21
22import java.io.Closeable;
23import java.io.EOFException;
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.InputStreamReader;
27import java.io.OutputStream;
28import java.io.OutputStreamWriter;
29import java.io.Reader;
30import java.io.StringReader;
31import java.io.Writer;
32import java.nio.CharBuffer;
33import java.nio.charset.Charset;
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.List;
37
38/**
39 * Provides utility methods for working with character streams.
40 *
41 * <p>All method parameters must be non-null unless documented otherwise.
42 *
43 * <p>Some of the methods in this class take arguments with a generic type of
44 * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
45 * those interfaces. Similarly for {@code Appendable & Closeable} and
46 * {@link java.io.Writer}.
47 *
48 * @author Chris Nokleberg
49 * @author Bin Zhu
50 * @since 1.0
51 */
52@Beta
53public final class CharStreams {
54  private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
55
56  private CharStreams() {}
57
58  /**
59   * Returns a factory that will supply instances of {@link StringReader} that
60   * read a string value.
61   *
62   * @param value the string to read
63   * @return the factory
64   */
65  public static InputSupplier<StringReader> newReaderSupplier(
66      final String value) {
67    Preconditions.checkNotNull(value);
68    return new InputSupplier<StringReader>() {
69      @Override
70      public StringReader getInput() {
71        return new StringReader(value);
72      }
73    };
74  }
75
76  /**
77   * Returns a factory that will supply instances of {@link InputStreamReader},
78   * using the given {@link InputStream} factory and character set.
79   *
80   * @param in the factory that will be used to open input streams
81   * @param charset the character set used to decode the input stream
82   * @return the factory
83   */
84  public static InputSupplier<InputStreamReader> newReaderSupplier(
85      final InputSupplier<? extends InputStream> in, final Charset charset) {
86    Preconditions.checkNotNull(in);
87    Preconditions.checkNotNull(charset);
88    return new InputSupplier<InputStreamReader>() {
89      @Override
90      public InputStreamReader getInput() throws IOException {
91        return new InputStreamReader(in.getInput(), charset);
92      }
93    };
94  }
95
96  /**
97   * Returns a factory that will supply instances of {@link OutputStreamWriter},
98   * using the given {@link OutputStream} factory and character set.
99   *
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    int successfulOps = 0;
151    R in = from.getInput();
152    try {
153      W out = to.getOutput();
154      try {
155        long count = copy(in, out);
156        successfulOps++;
157        return count;
158      } finally {
159        Closeables.close(out, successfulOps < 1);
160        successfulOps++;
161      }
162    } finally {
163      Closeables.close(in, successfulOps < 2);
164    }
165  }
166
167  /**
168   * Opens a {@link Readable} object from the supplier, copies all characters
169   * to the {@link Appendable} object, and closes the input. Does not close
170   * or flush the output.
171   *
172   * @param from the input factory
173   * @param to the object to write to
174   * @return the number of characters copied
175   * @throws IOException if an I/O error occurs
176   */
177  public static <R extends Readable & Closeable> long copy(
178      InputSupplier<R> from, Appendable to) throws IOException {
179    boolean threw = true;
180    R in = from.getInput();
181    try {
182      long count = copy(in, to);
183      threw = false;
184      return count;
185    } finally {
186      Closeables.close(in, threw);
187    }
188  }
189
190  /**
191   * Copies all characters between the {@link Readable} and {@link Appendable}
192   * objects. Does not close or flush either object.
193   *
194   * @param from the object to read from
195   * @param to the object to write to
196   * @return the number of characters copied
197   * @throws IOException if an I/O error occurs
198   */
199  public static long copy(Readable from, Appendable to) throws IOException {
200    CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
201    long total = 0;
202    while (true) {
203      int r = from.read(buf);
204      if (r == -1) {
205        break;
206      }
207      buf.flip();
208      to.append(buf, 0, r);
209      total += r;
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}
442