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