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