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 (from.read(buf) != -1) {
206 buf.flip();
207 to.append(buf);
208 total += buf.remaining();
209 buf.clear();
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 }