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