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 }