001 /*
002 * Copyright (C) 2007 Google Inc.
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
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 public StringReader getInput() {
070 return new StringReader(value);
071 }
072 };
073 }
074
075 /**
076 * Returns a factory that will supply instances of {@link InputStreamReader},
077 * using the given {@link InputStream} factory and character set.
078 *
079 * @param in the factory that will be used to open input streams
080 * @param charset the character set used to decode the input stream
081 * @return the factory
082 */
083 public static InputSupplier<InputStreamReader> newReaderSupplier(
084 final InputSupplier<? extends InputStream> in, final Charset charset) {
085 Preconditions.checkNotNull(in);
086 Preconditions.checkNotNull(charset);
087 return new InputSupplier<InputStreamReader>() {
088 public InputStreamReader getInput() throws IOException {
089 return new InputStreamReader(in.getInput(), charset);
090 }
091 };
092 }
093
094 /**
095 * Returns a factory that will supply instances of {@link OutputStreamWriter},
096 * using the given {@link OutputStream} factory and character set.
097 *
098 * @param out the factory that will be used to open output streams
099 * @param charset the character set used to encode the output stream
100 * @return the factory
101 */
102 public static OutputSupplier<OutputStreamWriter> newWriterSupplier(
103 final OutputSupplier<? extends OutputStream> out, final Charset charset) {
104 Preconditions.checkNotNull(out);
105 Preconditions.checkNotNull(charset);
106 return new OutputSupplier<OutputStreamWriter>() {
107 public OutputStreamWriter getOutput() throws IOException {
108 return new OutputStreamWriter(out.getOutput(), charset);
109 }
110 };
111 }
112
113 /**
114 * Writes a character sequence (such as a string) to an appendable
115 * object from the given supplier.
116 *
117 * @param from the character sequence to write
118 * @param to the output supplier
119 * @throws IOException if an I/O error occurs
120 */
121 public static <W extends Appendable & Closeable> void write(CharSequence from,
122 OutputSupplier<W> to) throws IOException {
123 Preconditions.checkNotNull(from);
124 boolean threw = true;
125 W out = to.getOutput();
126 try {
127 out.append(from);
128 threw = false;
129 } finally {
130 Closeables.close(out, threw);
131 }
132 }
133
134 /**
135 * Opens {@link Readable} and {@link Appendable} objects from the
136 * given factories, copies all characters between the two, and closes
137 * them.
138 *
139 * @param from the input factory
140 * @param to the output factory
141 * @return the number of characters copied
142 * @throws IOException if an I/O error occurs
143 */
144 public static <R extends Readable & Closeable,
145 W extends Appendable & Closeable> long copy(InputSupplier<R> from,
146 OutputSupplier<W> to) throws IOException {
147 boolean threw = true;
148 R in = from.getInput();
149 try {
150 W out = to.getOutput();
151 try {
152 long count = copy(in, out);
153 threw = false;
154 return count;
155 } finally {
156 Closeables.close(out, threw);
157 }
158 } finally {
159 Closeables.close(in, threw);
160 }
161 }
162
163 /**
164 * Opens a {@link Readable} object from the supplier, copies all characters
165 * to the {@link Appendable} object, and closes the input. Does not close
166 * or flush the output.
167 *
168 * @param from the input factory
169 * @param to the object to write to
170 * @return the number of characters copied
171 * @throws IOException if an I/O error occurs
172 */
173 public static <R extends Readable & Closeable> long copy(
174 InputSupplier<R> from, Appendable to) throws IOException {
175 boolean threw = true;
176 R in = from.getInput();
177 try {
178 long count = copy(in, to);
179 threw = false;
180 return count;
181 } finally {
182 Closeables.close(in, threw);
183 }
184 }
185
186 /**
187 * Copies all characters between the {@link Readable} and {@link Appendable}
188 * objects. Does not close or flush either object.
189 *
190 * @param from the object to read from
191 * @param to the object to write to
192 * @return the number of characters copied
193 * @throws IOException if an I/O error occurs
194 */
195 public static long copy(Readable from, Appendable to) throws IOException {
196 CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
197 long total = 0;
198 while (true) {
199 int r = from.read(buf);
200 if (r == -1) {
201 break;
202 }
203 buf.flip();
204 to.append(buf, 0, r);
205 total += r;
206 }
207 return total;
208 }
209
210 /**
211 * Reads all characters from a {@link Readable} object into a {@link String}.
212 * Does not close the {@code Readable}.
213 *
214 * @param r the object to read from
215 * @return a string containing all the characters
216 * @throws IOException if an I/O error occurs
217 */
218 public static String toString(Readable r) throws IOException {
219 return toStringBuilder(r).toString();
220 }
221
222 /**
223 * Returns the characters from a {@link Readable} & {@link Closeable} object
224 * supplied by a factory as a {@link String}.
225 *
226 * @param supplier the factory to read from
227 * @return a string containing all the characters
228 * @throws IOException if an I/O error occurs
229 */
230 public static <R extends Readable & Closeable> String toString(
231 InputSupplier<R> supplier) throws IOException {
232 return toStringBuilder(supplier).toString();
233 }
234
235 /**
236 * Reads all characters from a {@link Readable} object into a new
237 * {@link StringBuilder} instance. Does not close the {@code Readable}.
238 *
239 * @param r the object to read from
240 * @return a {@link StringBuilder} containing all the characters
241 * @throws IOException if an I/O error occurs
242 */
243 private static StringBuilder toStringBuilder(Readable r) throws IOException {
244 StringBuilder sb = new StringBuilder();
245 copy(r, sb);
246 return sb;
247 }
248
249 /**
250 * Returns the characters from a {@link Readable} & {@link Closeable} object
251 * supplied by a factory as a new {@link StringBuilder} instance.
252 *
253 * @param supplier the factory to read from
254 * @throws IOException if an I/O error occurs
255 */
256 private static <R extends Readable & Closeable> StringBuilder toStringBuilder(
257 InputSupplier<R> supplier) throws IOException {
258 boolean threw = true;
259 R r = supplier.getInput();
260 try {
261 StringBuilder result = toStringBuilder(r);
262 threw = false;
263 return result;
264 } finally {
265 Closeables.close(r, threw);
266 }
267 }
268
269 /**
270 * Reads the first line from a {@link Readable} & {@link Closeable} object
271 * supplied by a factory. The line does not include line-termination
272 * characters, but does include other leading and trailing whitespace.
273 *
274 * @param supplier the factory to read from
275 * @return the first line, or null if the reader is empty
276 * @throws IOException if an I/O error occurs
277 */
278 public static <R extends Readable & Closeable> String readFirstLine(
279 InputSupplier<R> supplier) throws IOException {
280 boolean threw = true;
281 R r = supplier.getInput();
282 try {
283 String line = new LineReader(r).readLine();
284 threw = false;
285 return line;
286 } finally {
287 Closeables.close(r, threw);
288 }
289 }
290
291 /**
292 * Reads all of the lines from a {@link Readable} & {@link Closeable} object
293 * supplied by a factory. The lines do not include line-termination
294 * characters, but do include other leading and trailing whitespace.
295 *
296 * @param supplier the factory to read from
297 * @return a mutable {@link List} containing all the lines
298 * @throws IOException if an I/O error occurs
299 */
300 public static <R extends Readable & Closeable> List<String> readLines(
301 InputSupplier<R> supplier) throws IOException {
302 boolean threw = true;
303 R r = supplier.getInput();
304 try {
305 List<String> result = readLines(r);
306 threw = false;
307 return result;
308 } finally {
309 Closeables.close(r, threw);
310 }
311 }
312
313 /**
314 * Reads all of the lines from a {@link Readable} object. The lines do
315 * not include line-termination characters, but do include other
316 * leading and trailing whitespace.
317 *
318 * <p>Does not close the {@code Readable}. If reading files or resources you
319 * should use the {@link Files#readLines} and {@link Resources#readLines}
320 * methods.
321 *
322 * @param r the object to read from
323 * @return a mutable {@link List} containing all the lines
324 * @throws IOException if an I/O error occurs
325 */
326 public static List<String> readLines(Readable r) throws IOException {
327 List<String> result = new ArrayList<String>();
328 LineReader lineReader = new LineReader(r);
329 String line;
330 while ((line = lineReader.readLine()) != null) {
331 result.add(line);
332 }
333 return result;
334 }
335
336 /**
337 * Streams lines from a {@link Readable} and {@link Closeable} object
338 * supplied by a factory, stopping when our callback returns false, or we
339 * have read all of the lines.
340 *
341 * @param supplier the factory to read from
342 * @param callback the LineProcessor to use to handle the lines
343 * @return the output of processing the lines
344 * @throws IOException if an I/O error occurs
345 */
346 public static <R extends Readable & Closeable, T> T readLines(
347 InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException {
348 boolean threw = true;
349 R r = supplier.getInput();
350 try {
351 LineReader lineReader = new LineReader(r);
352 String line;
353 while ((line = lineReader.readLine()) != null) {
354 if (!callback.processLine(line)) {
355 break;
356 }
357 }
358 threw = false;
359 } finally {
360 Closeables.close(r, threw);
361 }
362 return callback.getResult();
363 }
364
365 /**
366 * Joins multiple {@link Reader} suppliers into a single supplier.
367 * Reader returned from the supplier will contain the concatenated data
368 * from the readers of the underlying suppliers.
369 *
370 * <p>Reading from the joined reader will throw a {@link NullPointerException}
371 * if any of the suppliers are null or return null.
372 *
373 * <p>Only one underlying reader will be open at a time. Closing the
374 * joined reader will close the open underlying reader.
375 *
376 * @param suppliers the suppliers to concatenate
377 * @return a supplier that will return a reader containing the concatenated
378 * data
379 */
380 public static InputSupplier<Reader> join(
381 final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
382 return new InputSupplier<Reader>() {
383 @Override public Reader getInput() throws IOException {
384 return new MultiReader(suppliers.iterator());
385 }
386 };
387 }
388
389 /** Varargs form of {@link #join(Iterable)}. */
390 public static InputSupplier<Reader> join(
391 InputSupplier<? extends Reader>... suppliers) {
392 return join(Arrays.asList(suppliers));
393 }
394
395 /**
396 * Discards {@code n} characters of data from the reader. This method
397 * will block until the full amount has been skipped. Does not close the
398 * reader.
399 *
400 * @param reader the reader to read from
401 * @param n the number of characters to skip
402 * @throws EOFException if this stream reaches the end before skipping all
403 * the bytes
404 * @throws IOException if an I/O error occurs
405 */
406 public static void skipFully(Reader reader, long n) throws IOException {
407 while (n > 0) {
408 long amt = reader.skip(n);
409 if (amt == 0) {
410 // force a blocking read
411 if (reader.read() == -1) {
412 throw new EOFException();
413 }
414 n--;
415 } else {
416 n -= amt;
417 }
418 }
419 }
420
421 /**
422 * Returns a Writer that sends all output to the given {@link Appendable}
423 * target. Closing the writer will close the target if it is {@link
424 * Closeable}, and flushing the writer will flush the target if it is {@link
425 * java.io.Flushable}.
426 *
427 * @param target the object to which output will be sent
428 * @return a new Writer object, unless target is a Writer, in which case the
429 * target is returned
430 */
431 public static Writer asWriter(Appendable target) {
432 if (target instanceof Writer) {
433 return (Writer) target;
434 }
435 return new AppendableWriter(target);
436 }
437 }