/* * Copyright (C) 2012 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.io; import static com.google.common.base.Preconditions.checkNotNull; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import javax.annotation.Nullable; import com.google.common.annotations.Beta; import com.google.common.base.Ascii; import com.google.common.base.Splitter; import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; /** * A readable source of characters, such as a text file. Unlike a * {@link Reader}, a {@code CharSource} is not an open, stateful stream of * characters that can be read and closed. Instead, it is an immutable * supplier of {@code Reader} instances. * *
* {@code CharSource} provides two kinds of methods: *
* Several methods in this class, such as {@link #readLines()}, break the * contents of the source into lines. Like {@link BufferedReader}, these methods * break lines on any of {@code \n}, {@code \r} or {@code \r\n}, do not include * the line separator in each line and do not consider there to be an empty line * at the end if the contents are terminated with a line separator. * *
* Any {@link ByteSource} containing text encoded with a specific
* {@linkplain Charset character encoding} may be viewed as a {@code CharSource}
* using {@link ByteSource#asCharSource(Charset)}.
*
* @since 14.0
* @author Colin Decker
*/
public abstract class CharSource implements InputSupplier
* The caller is responsible for ensuring that the returned reader is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* reader
*/
public abstract Reader openStream() throws IOException;
/**
* This method is a temporary method provided for easing migration from
* suppliers to sources and sinks.
*
* @since 15.0
* @deprecated This method is only provided for temporary compatibility with the
* {@link InputSupplier} interface and should not be called
* directly. Use {@link #openStream} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Override
@Deprecated
public final Reader getInput() throws IOException {
return openStream();
}
/**
* Opens a new {@link BufferedReader} for reading from this source. This method
* should return a new, independent reader each time it is called.
*
*
* The caller is responsible for ensuring that the returned reader is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* reader
*/
public BufferedReader openBufferedStream() throws IOException {
Reader reader = openStream();
return (reader instanceof BufferedReader) ? (BufferedReader) reader : new BufferedReader(reader);
}
/**
* Appends the contents of this source to the given {@link Appendable} (such as
* a {@link Writer}). Does not close {@code appendable} if it is
* {@code Closeable}.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or writing to {@code appendable}
*/
public long copyTo(Appendable appendable) throws IOException {
checkNotNull(appendable);
Closer closer = Closer.create();
try {
Reader reader = closer.register(openStream());
return CharStreams.copy(reader, appendable);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Copies the contents of this source to the given sink.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or writing to {@code sink}
*/
public long copyTo(CharSink sink) throws IOException {
checkNotNull(sink);
Closer closer = Closer.create();
try {
Reader reader = closer.register(openStream());
Writer writer = closer.register(sink.openStream());
return CharStreams.copy(reader, writer);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads the contents of this source as a string.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source
*/
public String read() throws IOException {
Closer closer = Closer.create();
try {
Reader reader = closer.register(openStream());
return CharStreams.toString(reader);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads the first link of this source as a string. Returns {@code null} if this
* source is empty.
*
*
* Like {@link BufferedReader}, this method breaks lines on any of {@code \n},
* {@code \r} or {@code \r\n}, does not include the line separator in the
* returned line and does not consider there to be an extra empty line at the
* end if the content is terminated with a line separator.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source
*/
public @Nullable String readFirstLine() throws IOException {
Closer closer = Closer.create();
try {
BufferedReader reader = closer.register(openBufferedStream());
return reader.readLine();
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads all the lines of this source as a list of strings. The returned list
* will be empty if this source is empty.
*
*
* Like {@link BufferedReader}, this method breaks lines on any of {@code \n},
* {@code \r} or {@code \r\n}, does not include the line separator in the
* returned lines and does not consider there to be an extra empty line at the
* end if the content is terminated with a line separator.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source
*/
public ImmutableList
* Like {@link BufferedReader}, this method breaks lines on any of {@code \n},
* {@code \r} or {@code \r\n}, does not include the line separator in the lines
* passed to the {@code processor} and does not consider there to be an extra
* empty line at the end if the content is terminated with a line separator.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or if {@code processor} throws an
* {@code IOException}
* @since 16.0
*/
@Beta
public
* Only one underlying stream will be open at a time. Closing the concatenated
* stream will close the open underlying stream.
*
* @param sources the sources to concatenate
* @return a {@code CharSource} containing the concatenated data
* @since 15.0
*/
public static CharSource concat(Iterable extends CharSource> sources) {
return new ConcatenatedCharSource(sources);
}
/**
* Concatenates multiple {@link CharSource} instances into a single source.
* Streams returned from the source will contain the concatenated data from the
* streams of the underlying sources.
*
*
* Only one underlying stream will be open at a time. Closing the concatenated
* stream will close the open underlying stream.
*
*
* Note: The input {@code Iterator} will be copied to an {@code ImmutableList}
* when this method is called. This will fail if the iterator is infinite and
* may cause problems if the iterator eagerly fetches data for each source when
* iterated (rather than producing sources that only load data through their
* streams). Prefer using the {@link #concat(Iterable)} overload if possible.
*
* @param sources the sources to concatenate
* @return a {@code CharSource} containing the concatenated data
* @throws NullPointerException if any of {@code sources} is {@code null}
* @since 15.0
*/
public static CharSource concat(Iterator extends CharSource> sources) {
return concat(ImmutableList.copyOf(sources));
}
/**
* Concatenates multiple {@link CharSource} instances into a single source.
* Streams returned from the source will contain the concatenated data from the
* streams of the underlying sources.
*
*
* Only one underlying stream will be open at a time. Closing the concatenated
* stream will close the open underlying stream.
*
* @param sources the sources to concatenate
* @return a {@code CharSource} containing the concatenated data
* @throws NullPointerException if any of {@code sources} is {@code null}
* @since 15.0
*/
public static CharSource concat(CharSource... sources) {
return concat(ImmutableList.copyOf(sources));
}
/**
* Returns a view of the given character sequence as a {@link CharSource}. The
* behavior of the returned {@code CharSource} and any {@code Reader} instances
* created by it is unspecified if the {@code charSequence} is mutated while it
* is being read, so don't do that.
*
* @since 15.0 (since 14.0 as {@code CharStreams.asCharSource(String)})
*/
public static CharSource wrap(CharSequence charSequence) {
return new CharSequenceCharSource(charSequence);
}
/**
* Returns an immutable {@link CharSource} that contains no characters.
*
* @since 15.0
*/
public static CharSource empty() {
return EmptyCharSource.INSTANCE;
}
private static class CharSequenceCharSource extends CharSource {
private static final Splitter LINE_SPLITTER = Splitter.on(Pattern.compile("\r\n|\n|\r"));
private final CharSequence seq;
protected CharSequenceCharSource(CharSequence seq) {
this.seq = checkNotNull(seq);
}
@Override
public Reader openStream() {
return new CharSequenceReader(seq);
}
@Override
public String read() {
return seq.toString();
}
@Override
public boolean isEmpty() {
return seq.length() == 0;
}
/**
* Returns an iterable over the lines in the string. If the string ends in a
* newline, a final empty string is not included to match the behavior of
* BufferedReader/LineReader.readLine().
*/
private Iterable