1 /* 2 * Copyright (C) 2007 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.io; 18 19 import com.google.common.base.Preconditions; 20 21 import java.io.Closeable; 22 import java.io.EOFException; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.InputStreamReader; 26 import java.io.OutputStream; 27 import java.io.OutputStreamWriter; 28 import java.io.Reader; 29 import java.io.StringReader; 30 import java.io.Writer; 31 import java.nio.CharBuffer; 32 import java.nio.charset.Charset; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 37 /** 38 * Provides utility methods for working with character streams. 39 * 40 * <p>All method parameters must be non-null unless documented otherwise. 41 * 42 * <p>Some of the methods in this class take arguments with a generic type of 43 * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of 44 * those interfaces. Similarly for {@code Appendable & Closeable} and 45 * {@link java.io.Writer}. 46 * 47 * @author Chris Nokleberg 48 * @author Bin Zhu 49 * @since 2009.09.15 <b>tentative</b> 50 */ 51 public final class CharStreams { 52 private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes) 53 CharStreams()54 private CharStreams() {} 55 56 /** 57 * Returns a factory that will supply instances of {@link StringReader} that 58 * read a string value. 59 * 60 * @param value the string to read 61 * @return the factory 62 */ newReaderSupplier(final String value)63 public static InputSupplier<StringReader> newReaderSupplier(final String value) { 64 Preconditions.checkNotNull(value); 65 return new InputSupplier<StringReader>() { 66 public StringReader getInput() { 67 return new StringReader(value); 68 } 69 }; 70 } 71 72 /** 73 * Returns a factory that will supply instances of {@link InputStreamReader}, 74 * using the given {@link InputStream} factory and character set. 75 * 76 * @param in the factory that will be used to open input streams 77 * @param charset the character set used to decode the input stream 78 * @return the factory 79 */ 80 public static InputSupplier<InputStreamReader> newReaderSupplier( 81 final InputSupplier<? extends InputStream> in, final Charset charset) { 82 Preconditions.checkNotNull(in); 83 Preconditions.checkNotNull(charset); 84 return new InputSupplier<InputStreamReader>() { 85 public InputStreamReader getInput() throws IOException { 86 return new InputStreamReader(in.getInput(), charset); 87 } 88 }; 89 } 90 91 /** 92 * Returns a factory that will supply instances of {@link OutputStreamWriter}, 93 * using the given {@link OutputStream} factory and character set. 94 * 95 * @param out the factory that will be used to open output streams 96 * @param charset the character set used to encode the output stream 97 * @return the factory 98 */ 99 public static OutputSupplier<OutputStreamWriter> newWriterSupplier( 100 final OutputSupplier<? extends OutputStream> out, final Charset charset) { 101 Preconditions.checkNotNull(out); 102 Preconditions.checkNotNull(charset); 103 return new OutputSupplier<OutputStreamWriter>() { 104 public OutputStreamWriter getOutput() throws IOException { 105 return new OutputStreamWriter(out.getOutput(), charset); 106 } 107 }; 108 } 109 110 /** 111 * Writes a character sequence (such as a string) to an appendable 112 * object from the given supplier. 113 * 114 * @param from the character sequence to write 115 * @param to the output supplier 116 * @throws IOException if an I/O error occurs 117 */ 118 public static <W extends Appendable & Closeable> void write(CharSequence from, 119 OutputSupplier<W> to) throws IOException { 120 Preconditions.checkNotNull(from); 121 boolean threw = true; 122 W out = to.getOutput(); 123 try { 124 out.append(from); 125 threw = false; 126 } finally { 127 Closeables.close(out, threw); 128 } 129 } 130 131 /** 132 * Opens {@link Readable} and {@link Appendable} objects from the 133 * given factories, copies all characters between the two, and closes 134 * them. 135 * 136 * @param from the input factory 137 * @param to the output factory 138 * @return the number of characters copied 139 * @throws IOException if an I/O error occurs 140 */ 141 public static <R extends Readable & Closeable, 142 W extends Appendable & Closeable> long copy(InputSupplier<R> from, 143 OutputSupplier<W> to) throws IOException { 144 boolean threw = true; 145 R in = from.getInput(); 146 try { 147 W out = to.getOutput(); 148 try { 149 long count = copy(in, out); 150 threw = false; 151 return count; 152 } finally { 153 Closeables.close(out, threw); 154 } 155 } finally { 156 Closeables.close(in, threw); 157 } 158 } 159 160 /** 161 * Opens a {@link Readable} object from the supplier, copies all characters 162 * to the {@link Appendable} object, and closes the input. Does not close 163 * or flush the output. 164 * 165 * @param from the input factory 166 * @param to the object to write to 167 * @return the number of characters copied 168 * @throws IOException if an I/O error occurs 169 */ 170 public static <R extends Readable & Closeable> long copy(InputSupplier<R> from, 171 Appendable to) throws IOException { 172 boolean threw = true; 173 R in = from.getInput(); 174 try { 175 long count = copy(in, to); 176 threw = false; 177 return count; 178 } finally { 179 Closeables.close(in, threw); 180 } 181 } 182 183 /** 184 * Copies all characters between the {@link Readable} and {@link Appendable} 185 * objects. Does not close or flush either object. 186 * 187 * @param from the object to read from 188 * @param to the object to write to 189 * @return the number of characters copied 190 * @throws IOException if an I/O error occurs 191 */ 192 public static long copy(Readable from, Appendable to) throws IOException { 193 CharBuffer buf = CharBuffer.allocate(BUF_SIZE); 194 long total = 0; 195 while (true) { 196 int r = from.read(buf); 197 if (r == -1) { 198 break; 199 } 200 buf.flip(); 201 to.append(buf, 0, r); 202 total += r; 203 } 204 return total; 205 } 206 207 /** 208 * Reads all characters from a {@link Readable} object into a {@link String}. 209 * Does not close the {@code Readable}. 210 * 211 * @param r the object to read from 212 * @return a string containing all the characters 213 * @throws IOException if an I/O error occurs 214 */ 215 public static String toString(Readable r) throws IOException { 216 return toStringBuilder(r).toString(); 217 } 218 219 /** 220 * Returns the characters from a {@link Readable} & {@link Closeable} object 221 * supplied by a factory as a {@link String}. 222 * 223 * @param supplier the factory to read from 224 * @return a string containing all the characters 225 * @throws IOException if an I/O error occurs 226 */ 227 public static <R extends Readable & Closeable> String toString( 228 InputSupplier<R> supplier) throws IOException { 229 return toStringBuilder(supplier).toString(); 230 } 231 232 /** 233 * Reads all characters from a {@link Readable} object into a new 234 * {@link StringBuilder} instance. Does not close the {@code Readable}. 235 * 236 * @param r the object to read from 237 * @return a {@link StringBuilder} containing all the characters 238 * @throws IOException if an I/O error occurs 239 */ 240 private static StringBuilder toStringBuilder(Readable r) throws IOException { 241 StringBuilder sb = new StringBuilder(); 242 copy(r, sb); 243 return sb; 244 } 245 246 /** 247 * Returns the characters from a {@link Readable} & {@link Closeable} object 248 * supplied by a factory as a new {@link StringBuilder} instance. 249 * 250 * @param supplier the factory to read from 251 * @throws IOException if an I/O error occurs 252 */ 253 private static <R extends Readable & Closeable> StringBuilder toStringBuilder( 254 InputSupplier<R> supplier) throws IOException { 255 boolean threw = true; 256 R r = supplier.getInput(); 257 try { 258 StringBuilder result = toStringBuilder(r); 259 threw = false; 260 return result; 261 } finally { 262 Closeables.close(r, threw); 263 } 264 } 265 266 /** 267 * Reads the first line from a {@link Readable} & {@link Closeable} object 268 * supplied by a factory. The line does not include line-termination 269 * characters, but does include other leading and trailing whitespace. 270 * 271 * @param supplier the factory to read from 272 * @return the first line, or null if the reader is empty 273 * @throws IOException if an I/O error occurs 274 */ 275 public static <R extends Readable & Closeable> String readFirstLine( 276 InputSupplier<R> supplier) throws IOException { 277 boolean threw = true; 278 R r = supplier.getInput(); 279 try { 280 String line = new LineReader(r).readLine(); 281 threw = false; 282 return line; 283 } finally { 284 Closeables.close(r, threw); 285 } 286 } 287 288 /** 289 * Reads all of the lines from a {@link Readable} & {@link Closeable} object 290 * supplied by a factory. The lines do not include line-termination 291 * characters, but do include other leading and trailing whitespace. 292 * 293 * @param supplier the factory to read from 294 * @return a mutable {@link List} containing all the lines 295 * @throws IOException if an I/O error occurs 296 */ 297 public static <R extends Readable & Closeable> List<String> readLines( 298 InputSupplier<R> supplier) throws IOException { 299 boolean threw = true; 300 R r = supplier.getInput(); 301 try { 302 List<String> result = readLines(r); 303 threw = false; 304 return result; 305 } finally { 306 Closeables.close(r, threw); 307 } 308 } 309 310 /** 311 * Reads all of the lines from a {@link Readable} object. The lines do 312 * not include line-termination characters, but do include other 313 * leading and trailing whitespace. 314 * 315 * <p>Does not close the {@code Readable}. If reading files or resources you 316 * should use the {@link Files#readLines} and {@link Resources#readLines} 317 * methods. 318 * 319 * @param r the object to read from 320 * @return a mutable {@link List} containing all the lines 321 * @throws IOException if an I/O error occurs 322 */ 323 public static List<String> readLines(Readable r) throws IOException { 324 List<String> result = new ArrayList<String>(); 325 LineReader lineReader = new LineReader(r); 326 String line; 327 while ((line = lineReader.readLine()) != null) { 328 result.add(line); 329 } 330 return result; 331 } 332 333 /** 334 * Streams lines from a {@link Readable} and {@link Closeable} object 335 * supplied by a factory, stopping when our callback returns false, or we 336 * have read all of the lines. 337 * 338 * @param supplier the factory to read from 339 * @param callback the LineProcessor to use to handle the lines 340 * @return the output of processing the lines 341 * @throws IOException if an I/O error occurs 342 */ 343 public static <R extends Readable & Closeable, T> T readLines( 344 InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException { 345 boolean threw = true; 346 R r = supplier.getInput(); 347 try { 348 LineReader lineReader = new LineReader(r); 349 String line; 350 while ((line = lineReader.readLine()) != null) { 351 if (!callback.processLine(line)) { 352 break; 353 } 354 } 355 threw = false; 356 } finally { 357 Closeables.close(r, threw); 358 } 359 return callback.getResult(); 360 } 361 362 /** 363 * Joins multiple {@link Reader} suppliers into a single supplier. 364 * Reader returned from the supplier will contain the concatenated data 365 * from the readers of the underlying suppliers. 366 * 367 * <p>Reading from the joined reader will throw a {@link NullPointerException} 368 * if any of the suppliers are null or return null. 369 * 370 * <p>Only one underlying reader will be open at a time. Closing the 371 * joined reader will close the open underlying reader. 372 * 373 * @param suppliers the suppliers to concatenate 374 * @return a supplier that will return a reader containing the concatenated 375 * data 376 */ 377 public static InputSupplier<Reader> join( 378 final Iterable<? extends InputSupplier<? extends Reader>> suppliers) { 379 return new InputSupplier<Reader>() { 380 /*@Override*/ public Reader getInput() throws IOException { 381 return new MultiReader(suppliers.iterator()); 382 } 383 }; 384 } 385 386 /** Varargs form of {@link #join(Iterable)}. */ 387 public static InputSupplier<Reader> join( 388 InputSupplier<? extends Reader>... suppliers) { 389 return join(Arrays.asList(suppliers)); 390 } 391 392 /** 393 * Discards {@code n} characters of data from the reader. This method 394 * will block until the full amount has been skipped. Does not close the 395 * reader. 396 * 397 * @param reader the reader to read from 398 * @param n the number of characters to skip 399 * @throws EOFException if this stream reaches the end before skipping all 400 * the bytes 401 * @throws IOException if an I/O error occurs 402 */ 403 public static void skipFully(Reader reader, long n) throws IOException { 404 while (n > 0) { 405 long amt = reader.skip(n); 406 if (amt == 0) { 407 // force a blocking read 408 if (reader.read() == -1) { 409 throw new EOFException(); 410 } 411 n--; 412 } else { 413 n -= amt; 414 } 415 } 416 } 417 418 /** 419 * Returns a Writer that sends all output to the given {@link Appendable} 420 * target. Closing the writer will close the target if it is {@link 421 * Closeable}, and flushing the writer will flush the target if it is {@link 422 * java.io.Flushable}. 423 * 424 * @param target the object to which output will be sent 425 * @return a new Writer object, unless target is a Writer, in which case the 426 * target is returned 427 */ 428 public static Writer asWriter(Appendable target) { 429 if (target instanceof Writer) { 430 return (Writer) target; 431 } 432 return new AppendableWriter(target); 433 } 434 } 435