• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Guava Authors
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.annotations.Beta;
20 import com.google.common.base.Preconditions;
21 
22 import java.io.Closeable;
23 import java.io.EOFException;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.Reader;
30 import java.io.StringReader;
31 import java.io.Writer;
32 import java.nio.CharBuffer;
33 import java.nio.charset.Charset;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37 
38 /**
39  * Provides utility methods for working with character streams.
40  *
41  * <p>All method parameters must be non-null unless documented otherwise.
42  *
43  * <p>Some of the methods in this class take arguments with a generic type of
44  * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
45  * those interfaces. Similarly for {@code Appendable & Closeable} and
46  * {@link java.io.Writer}.
47  *
48  * @author Chris Nokleberg
49  * @author Bin Zhu
50  * @since 1.0
51  */
52 @Beta
53 public final class CharStreams {
54   private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
55 
CharStreams()56   private CharStreams() {}
57 
58   /**
59    * Returns a factory that will supply instances of {@link StringReader} that
60    * read a string value.
61    *
62    * @param value the string to read
63    * @return the factory
64    */
newReaderSupplier( final String value)65   public static InputSupplier<StringReader> newReaderSupplier(
66       final String value) {
67     Preconditions.checkNotNull(value);
68     return new InputSupplier<StringReader>() {
69       @Override
70       public StringReader getInput() {
71         return new StringReader(value);
72       }
73     };
74   }
75 
76   /**
77    * Returns a factory that will supply instances of {@link InputStreamReader},
78    * using the given {@link InputStream} factory and character set.
79    *
80    * @param in the factory that will be used to open input streams
81    * @param charset the character set used to decode the input stream
82    * @return the factory
83    */
84   public static InputSupplier<InputStreamReader> newReaderSupplier(
85       final InputSupplier<? extends InputStream> in, final Charset charset) {
86     Preconditions.checkNotNull(in);
87     Preconditions.checkNotNull(charset);
88     return new InputSupplier<InputStreamReader>() {
89       @Override
90       public InputStreamReader getInput() throws IOException {
91         return new InputStreamReader(in.getInput(), charset);
92       }
93     };
94   }
95 
96   /**
97    * Returns a factory that will supply instances of {@link OutputStreamWriter},
98    * using the given {@link OutputStream} factory and character set.
99    *
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     int successfulOps = 0;
151     R in = from.getInput();
152     try {
153       W out = to.getOutput();
154       try {
155         long count = copy(in, out);
156         successfulOps++;
157         return count;
158       } finally {
159         Closeables.close(out, successfulOps < 1);
160         successfulOps++;
161       }
162     } finally {
163       Closeables.close(in, successfulOps < 2);
164     }
165   }
166 
167   /**
168    * Opens a {@link Readable} object from the supplier, copies all characters
169    * to the {@link Appendable} object, and closes the input. Does not close
170    * or flush the output.
171    *
172    * @param from the input factory
173    * @param to the object to write to
174    * @return the number of characters copied
175    * @throws IOException if an I/O error occurs
176    */
177   public static <R extends Readable & Closeable> long copy(
178       InputSupplier<R> from, Appendable to) throws IOException {
179     boolean threw = true;
180     R in = from.getInput();
181     try {
182       long count = copy(in, to);
183       threw = false;
184       return count;
185     } finally {
186       Closeables.close(in, threw);
187     }
188   }
189 
190   /**
191    * Copies all characters between the {@link Readable} and {@link Appendable}
192    * objects. Does not close or flush either object.
193    *
194    * @param from the object to read from
195    * @param to the object to write to
196    * @return the number of characters copied
197    * @throws IOException if an I/O error occurs
198    */
199   public static long copy(Readable from, Appendable to) throws IOException {
200     CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
201     long total = 0;
202     while (true) {
203       int r = from.read(buf);
204       if (r == -1) {
205         break;
206       }
207       buf.flip();
208       to.append(buf, 0, r);
209       total += r;
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 }
442