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