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    }