    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 (from.read(buf) != -1) {
    206          buf.flip();
    207          to.append(buf);
    208          total += buf.remaining();
    209          buf.clear();
    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    }