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.Charsets;
021    import com.google.common.base.Preconditions;
022    
023    import java.io.Closeable;
024    import java.io.EOFException;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.io.InputStreamReader;
028    import java.io.OutputStream;
029    import java.io.OutputStreamWriter;
030    import java.io.Reader;
031    import java.io.StringReader;
032    import java.io.Writer;
033    import java.nio.CharBuffer;
034    import java.nio.charset.Charset;
035    import java.util.ArrayList;
036    import java.util.Arrays;
037    import 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
054    public 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          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 charset used to decode the input stream; see {@link
082       *     Charsets} for helpful predefined constants
083       * @return the factory
084       */
085      public static InputSupplier<InputStreamReader> newReaderSupplier(
086          final InputSupplier<? extends InputStream> in, final Charset charset) {
087        Preconditions.checkNotNull(in);
088        Preconditions.checkNotNull(charset);
089        return new InputSupplier<InputStreamReader>() {
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 charset used to encode the output stream; see {@link
102       *     Charsets} for helpful predefined constants
103       * @return the factory
104       */
105      public static OutputSupplier<OutputStreamWriter> newWriterSupplier(
106          final OutputSupplier<? extends OutputStream> out, final Charset charset) {
107        Preconditions.checkNotNull(out);
108        Preconditions.checkNotNull(charset);
109        return new OutputSupplier<OutputStreamWriter>() {
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 (from.read(buf) != -1) {
203          buf.flip();
204          to.append(buf);
205          total += buf.remaining();
206          buf.clear();
207        }
208        return total;
209      }
210    
211      /**
212       * Reads all characters from a {@link Readable} object into a {@link String}.
213       * Does not close the {@code Readable}.
214       *
215       * @param r the object to read from
216       * @return a string containing all the characters
217       * @throws IOException if an I/O error occurs
218       */
219      public static String toString(Readable r) throws IOException {
220        return toStringBuilder(r).toString();
221      }
222    
223      /**
224       * Returns the characters from a {@link Readable} & {@link Closeable} object
225       * supplied by a factory as a {@link String}.
226       *
227       * @param supplier the factory to read from
228       * @return a string containing all the characters
229       * @throws IOException if an I/O error occurs
230       */
231      public static <R extends Readable & Closeable> String toString(
232          InputSupplier<R> supplier) throws IOException {
233        return toStringBuilder(supplier).toString();
234      }
235    
236      /**
237       * Reads all characters from a {@link Readable} object into a new
238       * {@link StringBuilder} instance. Does not close the {@code Readable}.
239       *
240       * @param r the object to read from
241       * @return a {@link StringBuilder} containing all the characters
242       * @throws IOException if an I/O error occurs
243       */
244      private static StringBuilder toStringBuilder(Readable r) throws IOException {
245        StringBuilder sb = new StringBuilder();
246        copy(r, sb);
247        return sb;
248      }
249    
250      /**
251       * Returns the characters from a {@link Readable} & {@link Closeable} object
252       * supplied by a factory as a new {@link StringBuilder} instance.
253       *
254       * @param supplier the factory to read from
255       * @throws IOException if an I/O error occurs
256       */
257      private static <R extends Readable & Closeable> StringBuilder toStringBuilder(
258          InputSupplier<R> supplier) throws IOException {
259        boolean threw = true;
260        R r = supplier.getInput();
261        try {
262          StringBuilder result = toStringBuilder(r);
263          threw = false;
264          return result;
265        } finally {
266          Closeables.close(r, threw);
267        }
268      }
269    
270      /**
271       * Reads the first line from a {@link Readable} & {@link Closeable} object
272       * supplied by a factory. The line does not include line-termination
273       * characters, but does include other leading and trailing whitespace.
274       *
275       * @param supplier the factory to read from
276       * @return the first line, or null if the reader is empty
277       * @throws IOException if an I/O error occurs
278       */
279      public static <R extends Readable & Closeable> String readFirstLine(
280          InputSupplier<R> supplier) throws IOException {
281        boolean threw = true;
282        R r = supplier.getInput();
283        try {
284          String line = new LineReader(r).readLine();
285          threw = false;
286          return line;
287        } finally {
288          Closeables.close(r, threw);
289        }
290      }
291    
292      /**
293       * Reads all of the lines from a {@link Readable} & {@link Closeable} object
294       * supplied by a factory. The lines do not include line-termination
295       * characters, but do include other leading and trailing whitespace.
296       *
297       * @param supplier the factory 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 <R extends Readable & Closeable> List<String> readLines(
302          InputSupplier<R> supplier) throws IOException {
303        boolean threw = true;
304        R r = supplier.getInput();
305        try {
306          List<String> result = readLines(r);
307          threw = false;
308          return result;
309        } finally {
310          Closeables.close(r, threw);
311        }
312      }
313    
314      /**
315       * Reads all of the lines from a {@link Readable} object. The lines do
316       * not include line-termination characters, but do include other
317       * leading and trailing whitespace.
318       *
319       * <p>Does not close the {@code Readable}. If reading files or resources you
320       * should use the {@link Files#readLines} and {@link Resources#readLines}
321       * methods.
322       *
323       * @param r the object to read from
324       * @return a mutable {@link List} containing all the lines
325       * @throws IOException if an I/O error occurs
326       */
327      public static List<String> readLines(Readable r) throws IOException {
328        List<String> result = new ArrayList<String>();
329        LineReader lineReader = new LineReader(r);
330        String line;
331        while ((line = lineReader.readLine()) != null) {
332          result.add(line);
333        }
334        return result;
335      }
336    
337      /**
338       * Streams lines from a {@link Readable} and {@link Closeable} object
339       * supplied by a factory, stopping when our callback returns false, or we
340       * have read all of the lines.
341       *
342       * @param supplier the factory to read from
343       * @param callback the LineProcessor to use to handle the lines
344       * @return the output of processing the lines
345       * @throws IOException if an I/O error occurs
346       */
347      public static <R extends Readable & Closeable, T> T readLines(
348          InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException {
349        boolean threw = true;
350        R r = supplier.getInput();
351        try {
352          LineReader lineReader = new LineReader(r);
353          String line;
354          while ((line = lineReader.readLine()) != null) {
355            if (!callback.processLine(line)) {
356              break;
357            }
358          }
359          threw = false;
360        } finally {
361          Closeables.close(r, threw);
362        }
363        return callback.getResult();
364      }
365    
366      /**
367       * Joins multiple {@link Reader} suppliers into a single supplier.
368       * Reader returned from the supplier will contain the concatenated data
369       * from the readers of the underlying suppliers.
370       *
371       * <p>Reading from the joined reader will throw a {@link NullPointerException}
372       * if any of the suppliers are null or return null.
373       *
374       * <p>Only one underlying reader will be open at a time. Closing the
375       * joined reader will close the open underlying reader.
376       *
377       * @param suppliers the suppliers to concatenate
378       * @return a supplier that will return a reader containing the concatenated
379       *     data
380       */
381      public static InputSupplier<Reader> join(
382          final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
383        return new InputSupplier<Reader>() {
384          public Reader getInput() throws IOException {
385            return new MultiReader(suppliers.iterator());
386          }
387        };
388      }
389    
390      /** Varargs form of {@link #join(Iterable)}. */
391      public static InputSupplier<Reader> join(
392          InputSupplier<? extends Reader>... suppliers) {
393        return join(Arrays.asList(suppliers));
394      }
395    
396      /**
397       * Discards {@code n} characters of data from the reader. This method
398       * will block until the full amount has been skipped. Does not close the
399       * reader.
400       *
401       * @param reader the reader to read from
402       * @param n the number of characters to skip
403       * @throws EOFException if this stream reaches the end before skipping all
404       *     the bytes
405       * @throws IOException if an I/O error occurs
406       */
407      public static void skipFully(Reader reader, long n) throws IOException {
408        while (n > 0) {
409          long amt = reader.skip(n);
410          if (amt == 0) {
411            // force a blocking read
412            if (reader.read() == -1) {
413              throw new EOFException();
414            }
415            n--;
416          } else {
417            n -= amt;
418          }
419        }
420      }
421    
422      /**
423       * Returns a Writer that sends all output to the given {@link Appendable}
424       * target. Closing the writer will close the target if it is {@link
425       * Closeable}, and flushing the writer will flush the target if it is {@link
426       * java.io.Flushable}.
427       *
428       * @param target the object to which output will be sent
429       * @return a new Writer object, unless target is a Writer, in which case the
430       *     target is returned
431       */
432      public static Writer asWriter(Appendable target) {
433        if (target instanceof Writer) {
434          return (Writer) target;
435        }
436        return new AppendableWriter(target);
437      }
438    }