Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (C) 2007 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.io;
     18 
     19 import com.google.common.base.Preconditions;
     20 
     21 import java.io.Closeable;
     22 import java.io.EOFException;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.io.InputStreamReader;
     26 import java.io.OutputStream;
     27 import java.io.OutputStreamWriter;
     28 import java.io.Reader;
     29 import java.io.StringReader;
     30 import java.io.Writer;
     31 import java.nio.CharBuffer;
     32 import java.nio.charset.Charset;
     33 import java.util.ArrayList;
     34 import java.util.Arrays;
     35 import java.util.List;
     36 
     37 /**
     38  * Provides utility methods for working with character streams.
     39  *
     40  * <p>All method parameters must be non-null unless documented otherwise.
     41  *
     42  * <p>Some of the methods in this class take arguments with a generic type of
     43  * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
     44  * those interfaces. Similarly for {@code Appendable & Closeable} and
     45  * {@link java.io.Writer}.
     46  *
     47  * @author Chris Nokleberg
     48  * @author Bin Zhu
     49  * @since 2009.09.15 <b>tentative</b>
     50  */
     51 public final class CharStreams {
     52   private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
     53 
     54   private CharStreams() {}
     55 
     56   /**
     57    * Returns a factory that will supply instances of {@link StringReader} that
     58    * read a string value.
     59    *
     60    * @param value the string to read
     61    * @return the factory
     62    */
     63   public static InputSupplier<StringReader> newReaderSupplier(final String value) {
     64     Preconditions.checkNotNull(value);
     65     return new InputSupplier<StringReader>() {
     66       public StringReader getInput() {
     67         return new StringReader(value);
     68       }
     69     };
     70   }
     71 
     72   /**
     73    * Returns a factory that will supply instances of {@link InputStreamReader},
     74    * using the given {@link InputStream} factory and character set.
     75    *
     76    * @param in the factory that will be used to open input streams
     77    * @param charset the character set used to decode the input stream
     78    * @return the factory
     79    */
     80   public static InputSupplier<InputStreamReader> newReaderSupplier(
     81       final InputSupplier<? extends InputStream> in, final Charset charset) {
     82     Preconditions.checkNotNull(in);
     83     Preconditions.checkNotNull(charset);
     84     return new InputSupplier<InputStreamReader>() {
     85       public InputStreamReader getInput() throws IOException {
     86         return new InputStreamReader(in.getInput(), charset);
     87       }
     88     };
     89   }
     90 
     91   /**
     92    * Returns a factory that will supply instances of {@link OutputStreamWriter},
     93    * using the given {@link OutputStream} factory and character set.
     94    *
     95    * @param out the factory that will be used to open output streams
     96    * @param charset the character set used to encode the output stream
     97    * @return the factory
     98    */
     99   public static OutputSupplier<OutputStreamWriter> newWriterSupplier(
    100       final OutputSupplier<? extends OutputStream> out, final Charset charset) {
    101     Preconditions.checkNotNull(out);
    102     Preconditions.checkNotNull(charset);
    103     return new OutputSupplier<OutputStreamWriter>() {
    104       public OutputStreamWriter getOutput() throws IOException {
    105         return new OutputStreamWriter(out.getOutput(), charset);
    106       }
    107     };
    108   }
    109 
    110   /**
    111    * Writes a character sequence (such as a string) to an appendable
    112    * object from the given supplier.
    113    *
    114    * @param from the character sequence to write
    115    * @param to the output supplier
    116    * @throws IOException if an I/O error occurs
    117    */
    118   public static <W extends Appendable & Closeable> void write(CharSequence from,
    119       OutputSupplier<W> to) throws IOException {
    120     Preconditions.checkNotNull(from);
    121     boolean threw = true;
    122     W out = to.getOutput();
    123     try {
    124       out.append(from);
    125       threw = false;
    126     } finally {
    127       Closeables.close(out, threw);
    128     }
    129   }
    130 
    131   /**
    132    * Opens {@link Readable} and {@link Appendable} objects from the
    133    * given factories, copies all characters between the two, and closes
    134    * them.
    135    *
    136    * @param from the input factory
    137    * @param to the output factory
    138    * @return the number of characters copied
    139    * @throws IOException if an I/O error occurs
    140    */
    141   public static <R extends Readable & Closeable,
    142       W extends Appendable & Closeable> long copy(InputSupplier<R> from,
    143       OutputSupplier<W> to) throws IOException {
    144     boolean threw = true;
    145     R in = from.getInput();
    146     try {
    147       W out = to.getOutput();
    148       try {
    149         long count = copy(in, out);
    150         threw = false;
    151         return count;
    152       } finally {
    153         Closeables.close(out, threw);
    154       }
    155     } finally {
    156       Closeables.close(in, threw);
    157     }
    158   }
    159 
    160   /**
    161    * Opens a {@link Readable} object from the supplier, copies all characters
    162    * to the {@link Appendable} object, and closes the input. Does not close
    163    * or flush the output.
    164    *
    165    * @param from the input factory
    166    * @param to the object to write to
    167    * @return the number of characters copied
    168    * @throws IOException if an I/O error occurs
    169    */
    170   public static <R extends Readable & Closeable> long copy(InputSupplier<R> from,
    171       Appendable to) throws IOException {
    172     boolean threw = true;
    173     R in = from.getInput();
    174     try {
    175       long count = copy(in, to);
    176       threw = false;
    177       return count;
    178     } finally {
    179       Closeables.close(in, threw);
    180     }
    181   }
    182 
    183   /**
    184    * Copies all characters between the {@link Readable} and {@link Appendable}
    185    * objects. Does not close or flush either object.
    186    *
    187    * @param from the object to read from
    188    * @param to the object to write to
    189    * @return the number of characters copied
    190    * @throws IOException if an I/O error occurs
    191    */
    192   public static long copy(Readable from, Appendable to) throws IOException {
    193     CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
    194     long total = 0;
    195     while (true) {
    196       int r = from.read(buf);
    197       if (r == -1) {
    198         break;
    199       }
    200       buf.flip();
    201       to.append(buf, 0, r);
    202       total += r;
    203     }
    204     return total;
    205   }
    206 
    207   /**
    208    * Reads all characters from a {@link Readable} object into a {@link String}.
    209    * Does not close the {@code Readable}.
    210    *
    211    * @param r the object to read from
    212    * @return a string containing all the characters
    213    * @throws IOException if an I/O error occurs
    214    */
    215   public static String toString(Readable r) throws IOException {
    216     return toStringBuilder(r).toString();
    217   }
    218 
    219   /**
    220    * Returns the characters from a {@link Readable} & {@link Closeable} object
    221    * supplied by a factory as a {@link String}.
    222    *
    223    * @param supplier the factory to read from
    224    * @return a string containing all the characters
    225    * @throws IOException if an I/O error occurs
    226    */
    227   public static <R extends Readable & Closeable> String toString(
    228       InputSupplier<R> supplier) throws IOException {
    229     return toStringBuilder(supplier).toString();
    230   }
    231 
    232   /**
    233    * Reads all characters from a {@link Readable} object into a new
    234    * {@link StringBuilder} instance. Does not close the {@code Readable}.
    235    *
    236    * @param r the object to read from
    237    * @return a {@link StringBuilder} containing all the characters
    238    * @throws IOException if an I/O error occurs
    239    */
    240   private static StringBuilder toStringBuilder(Readable r) throws IOException {
    241     StringBuilder sb = new StringBuilder();
    242     copy(r, sb);
    243     return sb;
    244   }
    245 
    246   /**
    247    * Returns the characters from a {@link Readable} & {@link Closeable} object
    248    * supplied by a factory as a new {@link StringBuilder} instance.
    249    *
    250    * @param supplier the factory to read from
    251    * @throws IOException if an I/O error occurs
    252    */
    253   private static <R extends Readable & Closeable> StringBuilder toStringBuilder(
    254       InputSupplier<R> supplier) throws IOException {
    255     boolean threw = true;
    256     R r = supplier.getInput();
    257     try {
    258       StringBuilder result = toStringBuilder(r);
    259       threw = false;
    260       return result;
    261     } finally {
    262       Closeables.close(r, threw);
    263     }
    264   }
    265 
    266   /**
    267    * Reads the first line from a {@link Readable} & {@link Closeable} object
    268    * supplied by a factory. The line does not include line-termination
    269    * characters, but does include other leading and trailing whitespace.
    270    *
    271    * @param supplier the factory to read from
    272    * @return the first line, or null if the reader is empty
    273    * @throws IOException if an I/O error occurs
    274    */
    275   public static <R extends Readable & Closeable> String readFirstLine(
    276       InputSupplier<R> supplier) throws IOException {
    277     boolean threw = true;
    278     R r = supplier.getInput();
    279     try {
    280       String line = new LineReader(r).readLine();
    281       threw = false;
    282       return line;
    283     } finally {
    284       Closeables.close(r, threw);
    285     }
    286   }
    287 
    288   /**
    289    * Reads all of the lines from a {@link Readable} & {@link Closeable} object
    290    * supplied by a factory. The lines do not include line-termination
    291    * characters, but do include other leading and trailing whitespace.
    292    *
    293    * @param supplier the factory to read from
    294    * @return a mutable {@link List} containing all the lines
    295    * @throws IOException if an I/O error occurs
    296    */
    297   public static <R extends Readable & Closeable> List<String> readLines(
    298       InputSupplier<R> supplier) throws IOException {
    299     boolean threw = true;
    300     R r = supplier.getInput();
    301     try {
    302       List<String> result = readLines(r);
    303       threw = false;
    304       return result;
    305     } finally {
    306       Closeables.close(r, threw);
    307     }
    308   }
    309 
    310   /**
    311    * Reads all of the lines from a {@link Readable} object. The lines do
    312    * not include line-termination characters, but do include other
    313    * leading and trailing whitespace.
    314    *
    315    * <p>Does not close the {@code Readable}. If reading files or resources you
    316    * should use the {@link Files#readLines} and {@link Resources#readLines}
    317    * methods.
    318    *
    319    * @param r the object to read from
    320    * @return a mutable {@link List} containing all the lines
    321    * @throws IOException if an I/O error occurs
    322    */
    323   public static List<String> readLines(Readable r) throws IOException {
    324     List<String> result = new ArrayList<String>();
    325     LineReader lineReader = new LineReader(r);
    326     String line;
    327     while ((line = lineReader.readLine()) != null) {
    328       result.add(line);
    329     }
    330     return result;
    331   }
    332 
    333   /**
    334    * Streams lines from a {@link Readable} and {@link Closeable} object
    335    * supplied by a factory, stopping when our callback returns false, or we
    336    * have read all of the lines.
    337    *
    338    * @param supplier the factory to read from
    339    * @param callback the LineProcessor to use to handle the lines
    340    * @return the output of processing the lines
    341    * @throws IOException if an I/O error occurs
    342    */
    343   public static <R extends Readable & Closeable, T> T readLines(
    344       InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException {
    345     boolean threw = true;
    346     R r = supplier.getInput();
    347     try {
    348       LineReader lineReader = new LineReader(r);
    349       String line;
    350       while ((line = lineReader.readLine()) != null) {
    351         if (!callback.processLine(line)) {
    352           break;
    353         }
    354       }
    355       threw = false;
    356     } finally {
    357       Closeables.close(r, threw);
    358     }
    359     return callback.getResult();
    360   }
    361 
    362   /**
    363    * Joins multiple {@link Reader} suppliers into a single supplier.
    364    * Reader returned from the supplier will contain the concatenated data
    365    * from the readers of the underlying suppliers.
    366    *
    367    * <p>Reading from the joined reader will throw a {@link NullPointerException}
    368    * if any of the suppliers are null or return null.
    369    *
    370    * <p>Only one underlying reader will be open at a time. Closing the
    371    * joined reader will close the open underlying reader.
    372    *
    373    * @param suppliers the suppliers to concatenate
    374    * @return a supplier that will return a reader containing the concatenated
    375    *     data
    376    */
    377   public static InputSupplier<Reader> join(
    378       final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
    379     return new InputSupplier<Reader>() {
    380       /*@Override*/ public Reader getInput() throws IOException {
    381         return new MultiReader(suppliers.iterator());
    382       }
    383     };
    384   }
    385 
    386   /** Varargs form of {@link #join(Iterable)}. */
    387   public static InputSupplier<Reader> join(
    388       InputSupplier<? extends Reader>... suppliers) {
    389     return join(Arrays.asList(suppliers));
    390   }
    391 
    392   /**
    393    * Discards {@code n} characters of data from the reader. This method
    394    * will block until the full amount has been skipped. Does not close the
    395    * reader.
    396    *
    397    * @param reader the reader to read from
    398    * @param n the number of characters to skip
    399    * @throws EOFException if this stream reaches the end before skipping all
    400    *     the bytes
    401    * @throws IOException if an I/O error occurs
    402    */
    403   public static void skipFully(Reader reader, long n) throws IOException {
    404     while (n > 0) {
    405       long amt = reader.skip(n);
    406       if (amt == 0) {
    407         // force a blocking read
    408         if (reader.read() == -1) {
    409           throw new EOFException();
    410         }
    411         n--;
    412       } else {
    413         n -= amt;
    414       }
    415     }
    416   }
    417 
    418   /**
    419    * Returns a Writer that sends all output to the given {@link Appendable}
    420    * target. Closing the writer will close the target if it is {@link
    421    * Closeable}, and flushing the writer will flush the target if it is {@link
    422    * java.io.Flushable}.
    423    *
    424    * @param target the object to which output will be sent
    425    * @return a new Writer object, unless target is a Writer, in which case the
    426    *     target is returned
    427    */
    428   public static Writer asWriter(Appendable target) {
    429     if (target instanceof Writer) {
    430       return (Writer) target;
    431     }
    432     return new AppendableWriter(target);
    433   }
    434 }
    435