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 }