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.collect.ImmutableList;
022import com.google.common.collect.Lists;
023
024import java.io.BufferedReader;
025import java.io.IOException;
026import java.io.Reader;
027import java.io.Writer;
028import java.nio.charset.Charset;
029import java.util.List;
030
031import javax.annotation.Nullable;
032
033/**
034 * A readable source of characters, such as a text file. Unlike a {@link Reader}, a
035 * {@code CharSource} is not an open, stateful stream of characters that can be read and closed.
036 * Instead, it is an immutable <i>supplier</i> of {@code InputStream} instances.
037 *
038 * <p>{@code CharSource} provides two kinds of methods:
039 * <ul>
040 *   <li><b>Methods that return a reader:</b> These methods should return a <i>new</i>, independent
041 *   instance each time they are called. The caller is responsible for ensuring that the returned
042 *   reader is closed.
043 *   <li><b>Convenience methods:</b> These are implementations of common operations that are
044 *   typically implemented by opening a reader using one of the methods in the first category,
045 *   doing something and finally closing the reader that was opened.
046 * </ul>
047 *
048 * <p>Several methods in this class, such as {@link #readLines()}, break the contents of the
049 * source into lines. Like {@link BufferedReader}, these methods break lines on any of {@code \n},
050 * {@code \r} or {@code \r\n}, do not include the line separator in each line and do not consider
051 * there to be an empty line at the end if the contents are terminated with a line separator.
052 *
053 * <p>Any {@link ByteSource} containing text encoded with a specific {@linkplain Charset character
054 * encoding} may be viewed as a {@code CharSource} using {@link ByteSource#asCharSource(Charset)}.
055 *
056 * @since 14.0
057 * @author Colin Decker
058 */
059public abstract class CharSource {
060
061  /**
062   * Opens a new {@link Reader} for reading from this source. This method should return a new,
063   * independent reader each time it is called.
064   *
065   * <p>The caller is responsible for ensuring that the returned reader is closed.
066   *
067   * @throws IOException if an I/O error occurs in the process of opening the reader
068   */
069  public abstract Reader openStream() throws IOException;
070
071  /**
072   * Opens a new {@link BufferedReader} for reading from this source. This method should return a
073   * new, 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 BufferedReader openBufferedStream() throws IOException {
080    Reader reader = openStream();
081    return (reader instanceof BufferedReader)
082        ? (BufferedReader) reader
083        : new BufferedReader(reader);
084  }
085
086  /**
087   * Appends the contents of this source to the given {@link Appendable} (such as a {@link Writer}).
088   * Does not close {@code appendable} if it is {@code Closeable}.
089   *
090   * @throws IOException if an I/O error occurs in the process of reading from this source or
091   *     writing to {@code appendable}
092   */
093  public long copyTo(Appendable appendable) throws IOException {
094    checkNotNull(appendable);
095
096    Closer closer = Closer.create();
097    try {
098      Reader reader = closer.register(openStream());
099      return CharStreams.copy(reader, appendable);
100    } catch (Throwable e) {
101      throw closer.rethrow(e);
102    } finally {
103      closer.close();
104    }
105  }
106
107  /**
108   * Copies the contents of this source to the given sink.
109   *
110   * @throws IOException if an I/O error occurs in the process of reading from this source or
111   *     writing to {@code sink}
112   */
113  public long copyTo(CharSink sink) throws IOException {
114    checkNotNull(sink);
115
116    Closer closer = Closer.create();
117    try {
118      Reader reader = closer.register(openStream());
119      Writer writer = closer.register(sink.openStream());
120      return CharStreams.copy(reader, writer);
121    } catch (Throwable e) {
122      throw closer.rethrow(e);
123    } finally {
124      closer.close();
125    }
126  }
127
128  /**
129   * Reads the contents of this source as a string.
130   *
131   * @throws IOException if an I/O error occurs in the process of reading from this source
132   */
133  public String read() throws IOException {
134    Closer closer = Closer.create();
135    try {
136      Reader reader = closer.register(openStream());
137      return CharStreams.toString(reader);
138    } catch (Throwable e) {
139      throw closer.rethrow(e);
140    } finally {
141      closer.close();
142    }
143  }
144
145  /**
146   * Reads the first link of this source as a string. Returns {@code null} if this source is empty.
147   *
148   * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or
149   * {@code \r\n}, does not include the line separator in the returned line and does not consider
150   * there to be an extra empty line at the end if the content is terminated with a line separator.
151   *
152   * @throws IOException if an I/O error occurs in the process of reading from this source
153   */
154  public @Nullable String readFirstLine() throws IOException {
155    Closer closer = Closer.create();
156    try {
157      BufferedReader reader = closer.register(openBufferedStream());
158      return reader.readLine();
159    } catch (Throwable e) {
160      throw closer.rethrow(e);
161    } finally {
162      closer.close();
163    }
164  }
165
166  /**
167   * Reads all the lines of this source as a list of strings. The returned list will be empty if
168   * this source is empty.
169   *
170   * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or
171   * {@code \r\n}, does not include the line separator in the returned lines and does not consider
172   * there to be an extra empty line at the end if the content is terminated with a line separator.
173   *
174   * @throws IOException if an I/O error occurs in the process of reading from this source
175   */
176  public ImmutableList<String> readLines() throws IOException {
177    Closer closer = Closer.create();
178    try {
179      BufferedReader reader = closer.register(openBufferedStream());
180      List<String> result = Lists.newArrayList();
181      String line;
182      while ((line = reader.readLine()) != null) {
183        result.add(line);
184      }
185      return ImmutableList.copyOf(result);
186    } catch (Throwable e) {
187      throw closer.rethrow(e);
188    } finally {
189      closer.close();
190    }
191  }
192}