001/* 002 * Copyright (C) 2012 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 017package com.google.common.io; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.base.Splitter; 023import com.google.common.collect.AbstractIterator; 024import com.google.common.collect.ImmutableList; 025import com.google.common.collect.Lists; 026 027import java.io.BufferedReader; 028import java.io.IOException; 029import java.io.Reader; 030import java.io.Writer; 031import java.nio.charset.Charset; 032import java.util.Iterator; 033import java.util.List; 034import java.util.regex.Pattern; 035 036import javax.annotation.Nullable; 037 038/** 039 * A readable source of characters, such as a text file. Unlike a {@link Reader}, a 040 * {@code CharSource} is not an open, stateful stream of characters that can be read and closed. 041 * Instead, it is an immutable <i>supplier</i> of {@code Reader} instances. 042 * 043 * <p>{@code CharSource} provides two kinds of methods: 044 * <ul> 045 * <li><b>Methods that return a reader:</b> These methods should return a <i>new</i>, independent 046 * instance each time they are called. The caller is responsible for ensuring that the returned 047 * reader is closed. 048 * <li><b>Convenience methods:</b> These are implementations of common operations that are 049 * typically implemented by opening a reader using one of the methods in the first category, 050 * doing something and finally closing the reader that was opened. 051 * </ul> 052 * 053 * <p>Several methods in this class, such as {@link #readLines()}, break the contents of the 054 * source into lines. Like {@link BufferedReader}, these methods break lines on any of {@code \n}, 055 * {@code \r} or {@code \r\n}, do not include the line separator in each line and do not consider 056 * there to be an empty line at the end if the contents are terminated with a line separator. 057 * 058 * <p>Any {@link ByteSource} containing text encoded with a specific {@linkplain Charset character 059 * encoding} may be viewed as a {@code CharSource} using {@link ByteSource#asCharSource(Charset)}. 060 * 061 * @since 14.0 062 * @author Colin Decker 063 */ 064public abstract class CharSource implements InputSupplier<Reader> { 065 066 /** 067 * Constructor for use by subclasses. 068 */ 069 protected CharSource() {} 070 071 /** 072 * Opens a new {@link Reader} for reading from this source. This method should return a new, 073 * independent reader each time it is called. 074 * 075 * <p>The caller is responsible for ensuring that the returned reader is closed. 076 * 077 * @throws IOException if an I/O error occurs in the process of opening the reader 078 */ 079 public abstract Reader openStream() throws IOException; 080 081 /** 082 * This method is a temporary method provided for easing migration from suppliers to sources and 083 * sinks. 084 * 085 * @since 15.0 086 * @deprecated This method is only provided for temporary compatibility with the 087 * {@link InputSupplier} interface and should not be called directly. Use {@link #openStream} 088 * instead. This method is scheduled for removal in Guava 18.0. 089 */ 090 @Override 091 @Deprecated 092 public final Reader getInput() throws IOException { 093 return openStream(); 094 } 095 096 /** 097 * Opens a new {@link BufferedReader} for reading from this source. This method should return a 098 * new, independent reader each time it is called. 099 * 100 * <p>The caller is responsible for ensuring that the returned reader is closed. 101 * 102 * @throws IOException if an I/O error occurs in the process of opening the reader 103 */ 104 public BufferedReader openBufferedStream() throws IOException { 105 Reader reader = openStream(); 106 return (reader instanceof BufferedReader) 107 ? (BufferedReader) reader 108 : new BufferedReader(reader); 109 } 110 111 /** 112 * Appends the contents of this source to the given {@link Appendable} (such as a {@link Writer}). 113 * Does not close {@code appendable} if it is {@code Closeable}. 114 * 115 * @throws IOException if an I/O error occurs in the process of reading from this source or 116 * writing to {@code appendable} 117 */ 118 public long copyTo(Appendable appendable) throws IOException { 119 checkNotNull(appendable); 120 121 Closer closer = Closer.create(); 122 try { 123 Reader reader = closer.register(openStream()); 124 return CharStreams.copy(reader, appendable); 125 } catch (Throwable e) { 126 throw closer.rethrow(e); 127 } finally { 128 closer.close(); 129 } 130 } 131 132 /** 133 * Copies the contents of this source to the given sink. 134 * 135 * @throws IOException if an I/O error occurs in the process of reading from this source or 136 * writing to {@code sink} 137 */ 138 public long copyTo(CharSink sink) throws IOException { 139 checkNotNull(sink); 140 141 Closer closer = Closer.create(); 142 try { 143 Reader reader = closer.register(openStream()); 144 Writer writer = closer.register(sink.openStream()); 145 return CharStreams.copy(reader, writer); 146 } catch (Throwable e) { 147 throw closer.rethrow(e); 148 } finally { 149 closer.close(); 150 } 151 } 152 153 /** 154 * Reads the contents of this source as a string. 155 * 156 * @throws IOException if an I/O error occurs in the process of reading from this source 157 */ 158 public String read() throws IOException { 159 Closer closer = Closer.create(); 160 try { 161 Reader reader = closer.register(openStream()); 162 return CharStreams.toString(reader); 163 } catch (Throwable e) { 164 throw closer.rethrow(e); 165 } finally { 166 closer.close(); 167 } 168 } 169 170 /** 171 * Reads the first link of this source as a string. Returns {@code null} if this source is empty. 172 * 173 * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or 174 * {@code \r\n}, does not include the line separator in the returned line and does not consider 175 * there to be an extra empty line at the end if the content is terminated with a line separator. 176 * 177 * @throws IOException if an I/O error occurs in the process of reading from this source 178 */ 179 public @Nullable String readFirstLine() throws IOException { 180 Closer closer = Closer.create(); 181 try { 182 BufferedReader reader = closer.register(openBufferedStream()); 183 return reader.readLine(); 184 } catch (Throwable e) { 185 throw closer.rethrow(e); 186 } finally { 187 closer.close(); 188 } 189 } 190 191 /** 192 * Reads all the lines of this source as a list of strings. The returned list will be empty if 193 * this source is empty. 194 * 195 * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or 196 * {@code \r\n}, does not include the line separator in the returned lines and does not consider 197 * there to be an extra empty line at the end if the content is terminated with a line separator. 198 * 199 * @throws IOException if an I/O error occurs in the process of reading from this source 200 */ 201 public ImmutableList<String> readLines() throws IOException { 202 Closer closer = Closer.create(); 203 try { 204 BufferedReader reader = closer.register(openBufferedStream()); 205 List<String> result = Lists.newArrayList(); 206 String line; 207 while ((line = reader.readLine()) != null) { 208 result.add(line); 209 } 210 return ImmutableList.copyOf(result); 211 } catch (Throwable e) { 212 throw closer.rethrow(e); 213 } finally { 214 closer.close(); 215 } 216 } 217 218 /** 219 * Reads lines of text from this source, processing each line as it is read using the given 220 * {@link LineProcessor processor}. Stops when all lines have been processed or the processor 221 * returns {@code false} and returns the result produced by the processor. 222 * 223 * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or 224 * {@code \r\n}, does not include the line separator in the lines passed to the {@code processor} 225 * and does not consider there to be an extra empty line at the end if the content is terminated 226 * with a line separator. 227 * 228 * @throws IOException if an I/O error occurs in the process of reading from this source or if 229 * {@code processor} throws an {@code IOException} 230 * @since 16.0 231 */ 232 @Beta 233 public <T> T readLines(LineProcessor<T> processor) throws IOException { 234 checkNotNull(processor); 235 236 Closer closer = Closer.create(); 237 try { 238 Reader reader = closer.register(openStream()); 239 return CharStreams.readLines(reader, processor); 240 } catch (Throwable e) { 241 throw closer.rethrow(e); 242 } finally { 243 closer.close(); 244 } 245 } 246 247 /** 248 * Returns whether the source has zero chars. The default implementation is to open a stream and 249 * check for EOF. 250 * 251 * @throws IOException if an I/O error occurs 252 * @since 15.0 253 */ 254 public boolean isEmpty() throws IOException { 255 Closer closer = Closer.create(); 256 try { 257 Reader reader = closer.register(openStream()); 258 return reader.read() == -1; 259 } catch (Throwable e) { 260 throw closer.rethrow(e); 261 } finally { 262 closer.close(); 263 } 264 } 265 266 /** 267 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 268 * the source will contain the concatenated data from the streams of the underlying sources. 269 * 270 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 271 * close the open underlying stream. 272 * 273 * @param sources the sources to concatenate 274 * @return a {@code CharSource} containing the concatenated data 275 * @since 15.0 276 */ 277 public static CharSource concat(Iterable<? extends CharSource> sources) { 278 return new ConcatenatedCharSource(sources); 279 } 280 281 /** 282 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 283 * the source will contain the concatenated data from the streams of the underlying sources. 284 * 285 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 286 * close the open underlying stream. 287 * 288 * <p>Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this 289 * method is called. This will fail if the iterator is infinite and may cause problems if the 290 * iterator eagerly fetches data for each source when iterated (rather than producing sources 291 * that only load data through their streams). Prefer using the {@link #concat(Iterable)} 292 * overload if possible. 293 * 294 * @param sources the sources to concatenate 295 * @return a {@code CharSource} containing the concatenated data 296 * @throws NullPointerException if any of {@code sources} is {@code null} 297 * @since 15.0 298 */ 299 public static CharSource concat(Iterator<? extends CharSource> sources) { 300 return concat(ImmutableList.copyOf(sources)); 301 } 302 303 /** 304 * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from 305 * the source will contain the concatenated data from the streams of the underlying sources. 306 * 307 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 308 * close the open underlying stream. 309 * 310 * @param sources the sources to concatenate 311 * @return a {@code CharSource} containing the concatenated data 312 * @throws NullPointerException if any of {@code sources} is {@code null} 313 * @since 15.0 314 */ 315 public static CharSource concat(CharSource... sources) { 316 return concat(ImmutableList.copyOf(sources)); 317 } 318 319 /** 320 * Returns a view of the given character sequence as a {@link CharSource}. The behavior of the 321 * returned {@code CharSource} and any {@code Reader} instances created by it is unspecified if 322 * the {@code charSequence} is mutated while it is being read, so don't do that. 323 * 324 * @since 15.0 (since 14.0 as {@code CharStreams.asCharSource(String)}) 325 */ 326 public static CharSource wrap(CharSequence charSequence) { 327 return new CharSequenceCharSource(charSequence); 328 } 329 330 /** 331 * Returns an immutable {@link CharSource} that contains no characters. 332 * 333 * @since 15.0 334 */ 335 public static CharSource empty() { 336 return EmptyCharSource.INSTANCE; 337 } 338 339 private static class CharSequenceCharSource extends CharSource { 340 341 private static final Splitter LINE_SPLITTER 342 = Splitter.on(Pattern.compile("\r\n|\n|\r")); 343 344 private final CharSequence seq; 345 346 protected CharSequenceCharSource(CharSequence seq) { 347 this.seq = checkNotNull(seq); 348 } 349 350 @Override 351 public Reader openStream() { 352 return new CharSequenceReader(seq); 353 } 354 355 @Override 356 public String read() { 357 return seq.toString(); 358 } 359 360 @Override 361 public boolean isEmpty() { 362 return seq.length() == 0; 363 } 364 365 /** 366 * Returns an iterable over the lines in the string. If the string ends in 367 * a newline, a final empty string is not included to match the behavior of 368 * BufferedReader/LineReader.readLine(). 369 */ 370 private Iterable<String> lines() { 371 return new Iterable<String>() { 372 @Override 373 public Iterator<String> iterator() { 374 return new AbstractIterator<String>() { 375 Iterator<String> lines = LINE_SPLITTER.split(seq).iterator(); 376 377 @Override 378 protected String computeNext() { 379 if (lines.hasNext()) { 380 String next = lines.next(); 381 // skip last line if it's empty 382 if (lines.hasNext() || !next.isEmpty()) { 383 return next; 384 } 385 } 386 return endOfData(); 387 } 388 }; 389 } 390 }; 391 } 392 393 @Override 394 public String readFirstLine() { 395 Iterator<String> lines = lines().iterator(); 396 return lines.hasNext() ? lines.next() : null; 397 } 398 399 @Override 400 public ImmutableList<String> readLines() { 401 return ImmutableList.copyOf(lines()); 402 } 403 404 @Override 405 public <T> T readLines(LineProcessor<T> processor) throws IOException { 406 for (String line : lines()) { 407 if (!processor.processLine(line)) { 408 break; 409 } 410 } 411 return processor.getResult(); 412 } 413 414 @Override 415 public String toString() { 416 CharSequence shortened = (seq.length() <= 15) ? seq : seq.subSequence(0, 12) + "..."; 417 return "CharSource.wrap(" + shortened + ")"; 418 } 419 } 420 421 private static final class EmptyCharSource extends CharSequenceCharSource { 422 423 private static final EmptyCharSource INSTANCE = new EmptyCharSource(); 424 425 private EmptyCharSource() { 426 super(""); 427 } 428 429 @Override 430 public String toString() { 431 return "CharSource.empty()"; 432 } 433 } 434 435 private static final class ConcatenatedCharSource extends CharSource { 436 437 private final Iterable<? extends CharSource> sources; 438 439 ConcatenatedCharSource(Iterable<? extends CharSource> sources) { 440 this.sources = checkNotNull(sources); 441 } 442 443 @Override 444 public Reader openStream() throws IOException { 445 return new MultiReader(sources.iterator()); 446 } 447 448 @Override 449 public boolean isEmpty() throws IOException { 450 for (CharSource source : sources) { 451 if (!source.isEmpty()) { 452 return false; 453 } 454 } 455 return true; 456 } 457 458 @Override 459 public String toString() { 460 return "CharSource.concat(" + sources + ")"; 461 } 462 } 463}