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