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