001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.io; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018import static com.google.common.collect.Streams.stream; 019 020import com.google.common.annotations.GwtIncompatible; 021import com.google.common.annotations.J2ktIncompatible; 022import com.google.common.base.Ascii; 023import com.google.common.base.Optional; 024import com.google.common.base.Splitter; 025import com.google.common.collect.AbstractIterator; 026import com.google.common.collect.ImmutableList; 027import com.google.common.collect.Lists; 028import com.google.errorprone.annotations.CanIgnoreReturnValue; 029import com.google.errorprone.annotations.MustBeClosed; 030import java.io.BufferedReader; 031import java.io.Closeable; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.Reader; 035import java.io.StringReader; 036import java.io.UncheckedIOException; 037import java.io.Writer; 038import java.nio.charset.Charset; 039import java.util.Iterator; 040import java.util.List; 041import java.util.function.Consumer; 042import java.util.stream.Stream; 043import javax.annotation.CheckForNull; 044import org.checkerframework.checker.nullness.qual.Nullable; 045 046/** 047 * A readable source of characters, such as a text file. Unlike a {@link Reader}, a {@code 048 * CharSource} is not an open, stateful stream of characters that can be read and closed. Instead, 049 * it is an immutable <i>supplier</i> of {@code Reader} instances. 050 * 051 * <p>{@code CharSource} provides two kinds of methods: 052 * 053 * <ul> 054 * <li><b>Methods that return a reader:</b> These methods should return a <i>new</i>, independent 055 * instance each time they are called. The caller is responsible for ensuring that the 056 * returned reader is closed. 057 * <li><b>Convenience methods:</b> These are implementations of common operations that are 058 * typically implemented by opening a reader using one of the methods in the first category, 059 * doing something and finally closing the reader that was opened. 060 * </ul> 061 * 062 * <p>Several methods in this class, such as {@link #readLines()}, break the contents of the source 063 * into lines. Like {@link BufferedReader}, these methods break lines on any of {@code \n}, {@code 064 * \r} or {@code \r\n}, do not include the line separator in each line and do not consider there to 065 * be an empty line at the end if the contents are terminated with a line separator. 066 * 067 * <p>Any {@link ByteSource} containing text encoded with a specific {@linkplain Charset character 068 * encoding} may be viewed as a {@code CharSource} using {@link ByteSource#asCharSource(Charset)}. 069 * 070 * <p><b>Note:</b> In general, {@code CharSource} is intended to be used for "file-like" sources 071 * that provide readers that are: 072 * 073 * <ul> 074 * <li><b>Finite:</b> Many operations, such as {@link #length()} and {@link #read()}, will either 075 * block indefinitely or fail if the source creates an infinite reader. 076 * <li><b>Non-destructive:</b> A <i>destructive</i> reader will consume or otherwise alter the 077 * source as they are read from it. A source that provides such readers will not be reusable, 078 * and operations that read from the stream (including {@link #length()}, in some 079 * implementations) will prevent further operations from completing as expected. 080 * </ul> 081 * 082 * @since 14.0 083 * @author Colin Decker 084 */ 085@J2ktIncompatible 086@GwtIncompatible 087public abstract class CharSource { 088 089 /** Constructor for use by subclasses. */ 090 protected CharSource() {} 091 092 /** 093 * Returns a {@link ByteSource} view of this char source that encodes chars read from this source 094 * as bytes using the given {@link Charset}. 095 * 096 * <p>If {@link ByteSource#asCharSource} is called on the returned source with the same charset, 097 * the default implementation of this method will ensure that the original {@code CharSource} is 098 * returned, rather than round-trip encoding. Subclasses that override this method should behave 099 * the same way. 100 * 101 * @since 20.0 102 */ 103 public ByteSource asByteSource(Charset charset) { 104 return new AsByteSource(charset); 105 } 106 107 /** 108 * Opens a new {@link Reader} for reading from this source. This method returns a new, independent 109 * reader each time it is called. 110 * 111 * <p>The caller is responsible for ensuring that the returned reader is closed. 112 * 113 * @throws IOException if an I/O error occurs while opening the reader 114 */ 115 public abstract Reader openStream() throws IOException; 116 117 /** 118 * Opens a new {@link BufferedReader} for reading from this source. This method returns a new, 119 * independent reader each time it is called. 120 * 121 * <p>The caller is responsible for ensuring that the returned reader is closed. 122 * 123 * @throws IOException if an I/O error occurs while of opening the reader 124 */ 125 public BufferedReader openBufferedStream() throws IOException { 126 Reader reader = openStream(); 127 return (reader instanceof BufferedReader) 128 ? (BufferedReader) reader 129 : new BufferedReader(reader); 130 } 131 132 /** 133 * Opens a new {@link Stream} for reading text one line at a time from this source. This method 134 * returns a new, independent stream each time it is called. 135 * 136 * <p>The returned stream is lazy and only reads from the source in the terminal operation. If an 137 * I/O error occurs while the stream is reading from the source or when the stream is closed, an 138 * {@link UncheckedIOException} is thrown. 139 * 140 * <p>Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of 141 * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code 142 * \n}. If the source's content does not end in a line termination sequence, it is treated as if 143 * it does. 144 * 145 * <p>The caller is responsible for ensuring that the returned stream is closed. For example: 146 * 147 * <pre>{@code 148 * try (Stream<String> lines = source.lines()) { 149 * lines.map(...) 150 * .filter(...) 151 * .forEach(...); 152 * } 153 * }</pre> 154 * 155 * @throws IOException if an I/O error occurs while opening the stream 156 * @since 33.4.0 (but since 22.0 in the JRE flavor) 157 */ 158 @MustBeClosed 159 @SuppressWarnings("Java7ApiChecker") 160 // If users use this when they shouldn't, we hope that NewApi will catch subsequent Stream calls. 161 @IgnoreJRERequirement 162 public Stream<String> lines() throws IOException { 163 BufferedReader reader = openBufferedStream(); 164 return reader.lines().onClose(() -> closeUnchecked(reader)); 165 } 166 167 @SuppressWarnings("Java7ApiChecker") 168 @IgnoreJRERequirement // helper for lines() 169 /* 170 * If we make these calls inline inside the lambda inside lines(), we get an Animal Sniffer error, 171 * despite the @IgnoreJRERequirement annotation there. For details, see ImmutableSortedMultiset. 172 */ 173 private static void closeUnchecked(Closeable closeable) { 174 try { 175 closeable.close(); 176 } catch (IOException e) { 177 throw new UncheckedIOException(e); 178 } 179 } 180 181 /** 182 * Returns the size of this source in chars, if the size can be easily determined without actually 183 * opening the data stream. 184 * 185 * <p>The default implementation returns {@link Optional#absent}. Some sources, such as a {@code 186 * CharSequence}, may return a non-absent value. Note that in such cases, it is <i>possible</i> 187 * that this method will return a different number of chars than would be returned by reading all 188 * of the chars. 189 * 190 * <p>Additionally, for mutable sources such as {@code StringBuilder}s, a subsequent read may 191 * return a different number of chars if the contents are changed. 192 * 193 * @since 19.0 194 */ 195 public Optional<Long> lengthIfKnown() { 196 return Optional.absent(); 197 } 198 199 /** 200 * Returns the length of this source in chars, even if doing so requires opening and traversing an 201 * entire stream. To avoid a potentially expensive operation, see {@link #lengthIfKnown}. 202 * 203 * <p>The default implementation calls {@link #lengthIfKnown} and returns the value if present. If 204 * absent, it will fall back to a heavyweight operation that will open a stream, {@link 205 * Reader#skip(long) skip} to the end of the stream, and return the total number of chars that 206 * were skipped. 207 * 208 * <p>Note that for sources that implement {@link #lengthIfKnown} to provide a more efficient 209 * implementation, it is <i>possible</i> that this method will return a different number of chars 210 * than would be returned by reading all of the chars. 211 * 212 * <p>In either case, for mutable sources such as files, a subsequent read may return a different 213 * number of chars if the contents are changed. 214 * 215 * @throws IOException if an I/O error occurs while reading the length of this source 216 * @since 19.0 217 */ 218 public long length() throws IOException { 219 Optional<Long> lengthIfKnown = lengthIfKnown(); 220 if (lengthIfKnown.isPresent()) { 221 return lengthIfKnown.get(); 222 } 223 224 Closer closer = Closer.create(); 225 try { 226 Reader reader = closer.register(openStream()); 227 return countBySkipping(reader); 228 } catch (Throwable e) { 229 throw closer.rethrow(e); 230 } finally { 231 closer.close(); 232 } 233 } 234 235 private long countBySkipping(Reader reader) throws IOException { 236 long count = 0; 237 long read; 238 while ((read = reader.skip(Long.MAX_VALUE)) != 0) { 239 count += read; 240 } 241 return count; 242 } 243 244 /** 245 * Appends the contents of this source to the given {@link Appendable} (such as a {@link Writer}). 246 * Does not close {@code appendable} if it is {@code Closeable}. 247 * 248 * @return the number of characters copied 249 * @throws IOException if an I/O error occurs while reading from this source or writing to {@code 250 * appendable} 251 */ 252 @CanIgnoreReturnValue 253 public long copyTo(Appendable appendable) throws IOException { 254 checkNotNull(appendable); 255 256 Closer closer = Closer.create(); 257 try { 258 Reader reader = closer.register(openStream()); 259 return CharStreams.copy(reader, appendable); 260 } catch (Throwable e) { 261 throw closer.rethrow(e); 262 } finally { 263 closer.close(); 264 } 265 } 266 267 /** 268 * Copies the contents of this source to the given sink. 269 * 270 * @return the number of characters copied 271 * @throws IOException if an I/O error occurs while reading from this source or writing to {@code 272 * sink} 273 */ 274 @CanIgnoreReturnValue 275 public long copyTo(CharSink sink) throws IOException { 276 checkNotNull(sink); 277 278 Closer closer = Closer.create(); 279 try { 280 Reader reader = closer.register(openStream()); 281 Writer writer = closer.register(sink.openStream()); 282 return CharStreams.copy(reader, writer); 283 } catch (Throwable e) { 284 throw closer.rethrow(e); 285 } finally { 286 closer.close(); 287 } 288 } 289 290 /** 291 * Reads the contents of this source as a string. 292 * 293 * @throws IOException if an I/O error occurs while reading from this source 294 */ 295 public String read() throws IOException { 296 Closer closer = Closer.create(); 297 try { 298 Reader reader = closer.register(openStream()); 299 return CharStreams.toString(reader); 300 } catch (Throwable e) { 301 throw closer.rethrow(e); 302 } finally { 303 closer.close(); 304 } 305 } 306 307 /** 308 * Reads the first line of this source as a string. Returns {@code null} if this source is empty. 309 * 310 * <p>Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of 311 * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code 312 * \n}. If the source's content does not end in a line termination sequence, it is treated as if 313 * it does. 314 * 315 * @throws IOException if an I/O error occurs while reading from this source 316 */ 317 @CheckForNull 318 public String readFirstLine() throws IOException { 319 Closer closer = Closer.create(); 320 try { 321 BufferedReader reader = closer.register(openBufferedStream()); 322 return reader.readLine(); 323 } catch (Throwable e) { 324 throw closer.rethrow(e); 325 } finally { 326 closer.close(); 327 } 328 } 329 330 /** 331 * Reads all the lines of this source as a list of strings. The returned list will be empty if 332 * this source is empty. 333 * 334 * <p>Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of 335 * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code 336 * \n}. If the source's content does not end in a line termination sequence, it is treated as if 337 * it does. 338 * 339 * @throws IOException if an I/O error occurs while reading from this source 340 */ 341 public ImmutableList<String> readLines() throws IOException { 342 Closer closer = Closer.create(); 343 try { 344 BufferedReader reader = closer.register(openBufferedStream()); 345 List<String> result = Lists.newArrayList(); 346 String line; 347 while ((line = reader.readLine()) != null) { 348 result.add(line); 349 } 350 return ImmutableList.copyOf(result); 351 } catch (Throwable e) { 352 throw closer.rethrow(e); 353 } finally { 354 closer.close(); 355 } 356 } 357 358 /** 359 * Reads lines of text from this source, processing each line as it is read using the given {@link 360 * LineProcessor processor}. Stops when all lines have been processed or the processor returns 361 * {@code false} and returns the result produced by the processor. 362 * 363 * <p>Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of 364 * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code 365 * \n}. If the source's content does not end in a line termination sequence, it is treated as if 366 * it does. 367 * 368 * @throws IOException if an I/O error occurs while reading from this source or if {@code 369 * processor} throws an {@code IOException} 370 * @since 16.0 371 */ 372 @CanIgnoreReturnValue // some processors won't return a useful result 373 @ParametricNullness 374 public <T extends @Nullable Object> T readLines(LineProcessor<T> processor) throws IOException { 375 checkNotNull(processor); 376 377 Closer closer = Closer.create(); 378 try { 379 Reader reader = closer.register(openStream()); 380 return CharStreams.readLines(reader, processor); 381 } catch (Throwable e) { 382 throw closer.rethrow(e); 383 } finally { 384 closer.close(); 385 } 386 } 387 388 /** 389 * Reads all lines of text from this source, running the given {@code action} for each line as it 390 * is read. 391 * 392 * <p>Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of 393 * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code 394 * \n}. If the source's content does not end in a line termination sequence, it is treated as if 395 * it does. 396 * 397 * @throws IOException if an I/O error occurs while reading from this source or if {@code action} 398 * throws an {@code UncheckedIOException} 399 * @since 33.4.0 (but since 22.0 in the JRE flavor) 400 */ 401 @SuppressWarnings("Java7ApiChecker") 402 /* 403 * We have to rely on users not to call this without library desugaring, as NewApi won't flag 404 * Consumer creation. 405 */ 406 @IgnoreJRERequirement 407 public void forEachLine(Consumer<? super String> action) throws IOException { 408 try (Stream<String> lines = lines()) { 409 // The lines should be ordered regardless in most cases, but use forEachOrdered to be sure 410 lines.forEachOrdered(action); 411 } catch (UncheckedIOException e) { 412 throw e.getCause(); 413 } 414 } 415 416 /** 417 * Returns whether the source has zero chars. The default implementation first checks {@link 418 * #lengthIfKnown}, returning true if it's known to be zero and false if it's known to be 419 * non-zero. If the length is not known, it falls back to opening a stream and checking for EOF. 420 * 421 * <p>Note that, in cases where {@code lengthIfKnown} returns zero, it is <i>possible</i> that 422 * chars are actually available for reading. This means that a source may return {@code true} from 423 * {@code isEmpty()} despite having readable content. 424 * 425 * @throws IOException if an I/O error occurs 426 * @since 15.0 427 */ 428 public boolean isEmpty() throws IOException { 429 Optional<Long> lengthIfKnown = lengthIfKnown(); 430 if (lengthIfKnown.isPresent()) { 431 return lengthIfKnown.get() == 0L; 432 } 433 Closer closer = Closer.create(); 434 try { 435 Reader reader = closer.register(openStream()); 436 return reader.read() == -1; 437 } catch (Throwable e) { 438 throw closer.rethrow(e); 439 } finally { 440 closer.close(); 441 } 442 } 443 444 /** 445 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 446 * the source will contain the concatenated data from the streams of the underlying sources. 447 * 448 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 449 * close the open underlying stream. 450 * 451 * @param sources the sources to concatenate 452 * @return a {@code CharSource} containing the concatenated data 453 * @since 15.0 454 */ 455 public static CharSource concat(Iterable<? extends CharSource> sources) { 456 return new ConcatenatedCharSource(sources); 457 } 458 459 /** 460 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 461 * the source will contain the concatenated data from the streams of the underlying sources. 462 * 463 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 464 * close the open underlying stream. 465 * 466 * <p>Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this method 467 * is called. This will fail if the iterator is infinite and may cause problems if the iterator 468 * eagerly fetches data for each source when iterated (rather than producing sources that only 469 * load data through their streams). Prefer using the {@link #concat(Iterable)} overload if 470 * possible. 471 * 472 * @param sources the sources to concatenate 473 * @return a {@code CharSource} containing the concatenated data 474 * @throws NullPointerException if any of {@code sources} is {@code null} 475 * @since 15.0 476 */ 477 public static CharSource concat(Iterator<? extends CharSource> sources) { 478 return concat(ImmutableList.copyOf(sources)); 479 } 480 481 /** 482 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 483 * the source will contain the concatenated data from the streams of the underlying sources. 484 * 485 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 486 * close the open underlying stream. 487 * 488 * @param sources the sources to concatenate 489 * @return a {@code CharSource} containing the concatenated data 490 * @throws NullPointerException if any of {@code sources} is {@code null} 491 * @since 15.0 492 */ 493 public static CharSource concat(CharSource... sources) { 494 return concat(ImmutableList.copyOf(sources)); 495 } 496 497 /** 498 * Returns a view of the given character sequence as a {@link CharSource}. The behavior of the 499 * returned {@code CharSource} and any {@code Reader} instances created by it is unspecified if 500 * the {@code charSequence} is mutated while it is being read, so don't do that. 501 * 502 * @since 15.0 (since 14.0 as {@code CharStreams.asCharSource(String)}) 503 */ 504 public static CharSource wrap(CharSequence charSequence) { 505 return charSequence instanceof String 506 ? new StringCharSource((String) charSequence) 507 : new CharSequenceCharSource(charSequence); 508 } 509 510 /** 511 * Returns an immutable {@link CharSource} that contains no characters. 512 * 513 * @since 15.0 514 */ 515 public static CharSource empty() { 516 return EmptyCharSource.INSTANCE; 517 } 518 519 /** A byte source that reads chars from this source and encodes them as bytes using a charset. */ 520 private final class AsByteSource extends ByteSource { 521 522 final Charset charset; 523 524 AsByteSource(Charset charset) { 525 this.charset = checkNotNull(charset); 526 } 527 528 @Override 529 public CharSource asCharSource(Charset charset) { 530 if (charset.equals(this.charset)) { 531 return CharSource.this; 532 } 533 return super.asCharSource(charset); 534 } 535 536 @Override 537 public InputStream openStream() throws IOException { 538 return new ReaderInputStream(CharSource.this.openStream(), charset, 8192); 539 } 540 541 @Override 542 public String toString() { 543 return CharSource.this.toString() + ".asByteSource(" + charset + ")"; 544 } 545 } 546 547 private static class CharSequenceCharSource extends CharSource { 548 549 private static final Splitter LINE_SPLITTER = Splitter.onPattern("\r\n|\n|\r"); 550 551 protected final CharSequence seq; 552 553 protected CharSequenceCharSource(CharSequence seq) { 554 this.seq = checkNotNull(seq); 555 } 556 557 @Override 558 public Reader openStream() { 559 return new CharSequenceReader(seq); 560 } 561 562 @Override 563 public String read() { 564 return seq.toString(); 565 } 566 567 @Override 568 public boolean isEmpty() { 569 return seq.length() == 0; 570 } 571 572 @Override 573 public long length() { 574 return seq.length(); 575 } 576 577 @Override 578 public Optional<Long> lengthIfKnown() { 579 return Optional.of((long) seq.length()); 580 } 581 582 /** 583 * Returns an iterator over the lines in the string. If the string ends in a newline, a final 584 * empty string is not included, to match the behavior of BufferedReader/LineReader.readLine(). 585 */ 586 private Iterator<String> linesIterator() { 587 return new AbstractIterator<String>() { 588 Iterator<String> lines = LINE_SPLITTER.split(seq).iterator(); 589 590 @Override 591 @CheckForNull 592 protected String computeNext() { 593 if (lines.hasNext()) { 594 String next = lines.next(); 595 // skip last line if it's empty 596 if (lines.hasNext() || !next.isEmpty()) { 597 return next; 598 } 599 } 600 return endOfData(); 601 } 602 }; 603 } 604 605 @Override 606 @SuppressWarnings("Java7ApiChecker") 607 // If users use this when they shouldn't, we hope that NewApi will catch subsequent Stream calls 608 @IgnoreJRERequirement 609 public Stream<String> lines() { 610 return stream(linesIterator()); 611 } 612 613 @Override 614 @CheckForNull 615 public String readFirstLine() { 616 Iterator<String> lines = linesIterator(); 617 return lines.hasNext() ? lines.next() : null; 618 } 619 620 @Override 621 public ImmutableList<String> readLines() { 622 return ImmutableList.copyOf(linesIterator()); 623 } 624 625 @Override 626 @ParametricNullness 627 public <T extends @Nullable Object> T readLines(LineProcessor<T> processor) throws IOException { 628 Iterator<String> lines = linesIterator(); 629 while (lines.hasNext()) { 630 if (!processor.processLine(lines.next())) { 631 break; 632 } 633 } 634 return processor.getResult(); 635 } 636 637 @Override 638 public String toString() { 639 return "CharSource.wrap(" + Ascii.truncate(seq, 30, "...") + ")"; 640 } 641 } 642 643 /** 644 * Subclass specialized for string instances. 645 * 646 * <p>Since Strings are immutable and built into the jdk we can optimize some operations 647 * 648 * <ul> 649 * <li>use {@link StringReader} instead of {@link CharSequenceReader}. It is faster since it can 650 * use {@link String#getChars(int, int, char[], int)} instead of copying characters one by 651 * one with {@link CharSequence#charAt(int)}. 652 * <li>use {@link Appendable#append(CharSequence)} in {@link #copyTo(Appendable)} and {@link 653 * #copyTo(CharSink)}. We know this is correct since strings are immutable and so the length 654 * can't change, and it is faster because many writers and appendables are optimized for 655 * appending string instances. 656 * </ul> 657 */ 658 private static class StringCharSource extends CharSequenceCharSource { 659 protected StringCharSource(String seq) { 660 super(seq); 661 } 662 663 @Override 664 public Reader openStream() { 665 return new StringReader((String) seq); 666 } 667 668 @Override 669 public long copyTo(Appendable appendable) throws IOException { 670 appendable.append(seq); 671 return seq.length(); 672 } 673 674 @Override 675 public long copyTo(CharSink sink) throws IOException { 676 checkNotNull(sink); 677 Closer closer = Closer.create(); 678 try { 679 Writer writer = closer.register(sink.openStream()); 680 writer.write((String) seq); 681 return seq.length(); 682 } catch (Throwable e) { 683 throw closer.rethrow(e); 684 } finally { 685 closer.close(); 686 } 687 } 688 } 689 690 private static final class EmptyCharSource extends StringCharSource { 691 692 private static final EmptyCharSource INSTANCE = new EmptyCharSource(); 693 694 private EmptyCharSource() { 695 super(""); 696 } 697 698 @Override 699 public String toString() { 700 return "CharSource.empty()"; 701 } 702 } 703 704 private static final class ConcatenatedCharSource extends CharSource { 705 706 private final Iterable<? extends CharSource> sources; 707 708 ConcatenatedCharSource(Iterable<? extends CharSource> sources) { 709 this.sources = checkNotNull(sources); 710 } 711 712 @Override 713 public Reader openStream() throws IOException { 714 return new MultiReader(sources.iterator()); 715 } 716 717 @Override 718 public boolean isEmpty() throws IOException { 719 for (CharSource source : sources) { 720 if (!source.isEmpty()) { 721 return false; 722 } 723 } 724 return true; 725 } 726 727 @Override 728 public Optional<Long> lengthIfKnown() { 729 long result = 0L; 730 for (CharSource source : sources) { 731 Optional<Long> lengthIfKnown = source.lengthIfKnown(); 732 if (!lengthIfKnown.isPresent()) { 733 return Optional.absent(); 734 } 735 result += lengthIfKnown.get(); 736 } 737 return Optional.of(result); 738 } 739 740 @Override 741 public long length() throws IOException { 742 long result = 0L; 743 for (CharSource source : sources) { 744 result += source.length(); 745 } 746 return result; 747 } 748 749 @Override 750 public String toString() { 751 return "CharSource.concat(" + sources + ")"; 752 } 753 } 754}