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